Java Thread系列(三)线程安全
Java Thread系列(三)线程安全
一、什么是线程安全
线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
线程安全来说,需要满足以下两个特性:
原子性
可见性
public class MyThread extends Thread {
private int count = 5;
//synchronized加锁 同步锁
public /*synchronized*/ void run () {
System.out.printf("线程%s执行,count:%s\n", this.currentThread().getName(),--count);
}
public static void main(String[] args) {
/**
* 当多个线程访问 MyThread 的 run 方法时,以排队的方式进行处理(这里的排序是按照 CPU 分配的先后顺序而定的)
* 一个线程要执行 synchronized 修饰的方法时
* 1. 尝试获得锁,如果拿到锁则执行该方法
* 2. 这个线程就会不断尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
*/
MyThread thread = new MyThread();
new Thread(thread, "t1").start();
new Thread(thread, "t2").start();
new Thread(thread, "t3").start();
new Thread(thread, "t4").start();
new Thread(thread, "t5").start();
new Thread(thread, "t6").start();
}
}
执行结果:
//MyThread 的 run() 方法不加锁 synchronized
线程t1执行,count:4
线程t4执行,count:1
线程t5执行,count:1
线程t3执行,count:2
线程t2执行,count:3
//run() 加锁 synchronized,期待的结果
线程t1执行,count:4
线程t2执行,count:3
线程t5执行,count:2
线程t4执行,count:1
线程t3执行,count:0
由此可见:
- 多个线程要执行 synchronized 修饰的方法时,必须获取对象锁,如果得不到这把锁,就处于等待状态,直到获取这把锁才执行这个方法。
二、什么是锁:对象锁和类锁
多个线程多个锁,多个线程,每个线程都可以拿到自己指定的锁,分别获得锁后执行 synchronized 修辞的方法。
同步(synchronized)的概念就是共享,我们要牢牢记住“共享”这两个字,如果不是共享的资源,就没有必要进行同步。
异步(asynchronized)的概念就是独立,相互之间不受任何制约。eg: Ajax
public class MutiThread {
private /*static*/ int num = 0;
/**
* synchronized:对象锁,两个对象,线程获得的就是两个不同的锁,互不影响
* static synchronized:表示类级别的锁,即便多个对象也是相同的锁
*/
public /*static*/ synchronized void printNum (String tag) {
try {
if("a".equalsIgnoreCase(tag)) {
System.out.printf("tag %s 设置成功\n", tag);
num = 100;
Thread.sleep(1000);
} else {
System.out.printf("tag %s 设置成功\n", tag);
num = 200;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("tag %s , num=%s\n", tag, num);
}
public static void main(String[] args) {
final MutiThread m1 = new MutiThread();
final MutiThread m2 = new MutiThread();
new Thread(new Runnable() {
public void run() {
m1.printNum("a");
}
}).start();
new Thread(new Runnable() {
public void run() {
m1.printNum("b");
}
}).start();
}
}
执行结果:
//run()方法用synchronized修辞
tag b 设置成功
tag b , num=200
tag a 设置成功
tag a , num=100
//run()方法用static synchronized修辞
tag a 设置成功
tag a , num=100
tag b 设置成功
tag b , num=200
由此可见:
static synchronized 修辞的方法,属于类级别的锁,多个对象共享同一把锁。
synchronized 修辞的方法,属于对象锁,一个对象一把锁。
三、脏读
对于对象的同步和异步的方法,一定要考虑问题的整体性,不然就会出现数据不一致的错误,很经典的错误就是脏读(dirtyread)
public class DirtyRead {
private String username;
private String password;
public /*synchronized*/ void setValue(String username, String password) {
this.username = username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
//e.printStackTrace();
}
this.password = password;
System.out.printf("username=%s; password=%s\n", username, password);
}
public /*synchronized*/ void getValue() {
System.out.printf("username=%s; password=%s\n", username, password);
}
public static void main(String[] args) throws InterruptedException {
final DirtyRead dirtyRead = new DirtyRead();
new Thread(new Runnable() {
public void run() {
dirtyRead.setValue("admin", "admin");
}
}).start();
Thread.sleep(1000);
dirtyRead.getValue();
//username=admin; password=null 不加锁,产生脏读
//username=admin; password=admin 加锁
}
}
执行结果:
setValue() 执行要 2s,而主程序 1s 时调用 getValue() ,这时username 已经赋值,而 password 仍未赋值,产生了脏读,username=admin; password=null
setValue() 和 getValue() 都对 username 和 password 操作,所以要避免产生脏读,需要对这两个方法都加锁 synchronized。
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java Thread系列(三)线程安全的更多相关文章
- Java Thread系列(四)线程通信
Java Thread系列(四)线程通信 一.传统通信 public static void main(String[] args) { //volatile实现两个线程间数据可见性 private ...
- Java Thread系列(二)线程状态
Java Thread系列(二)线程状态 一.线程的五种状态 新建状态(New):新创建了一个线程对象,尚未启动. 就绪状态(Runnable):也叫可运行状态.线程对象创建后,其他线程调用了该对象的 ...
- Java Thread系列(一)线程创建
Java Thread系列(一)线程创建 Java 中创建线程主要有三种方式:继承 Thread.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实 ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java Thread系列(十)Future 模式
Java Thread系列(十)Future 模式 Future 模式适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量. 一.Future 模式核心思想 如下的请求 ...
- java: Thread 和 runnable线程类
java: Thread 和 runnable线程类 Java有2种实现线程的方法:Thread类,Runnable接口.(其实Thread本身就是Runnable的子类) Thread类,默认有ru ...
随机推荐
- FastAdmin 升级后出现 is already in use
FastAdmin 升级后出现 is already in use 升级 FastAdmin 改进很多,但全新安装出现以下错误 Cannot use app\common\library\Menu a ...
- vCenter 6.5安装
http://guanjianfeng.com/archives/1160269 最近,VMware发布了vSphere 6.5版本,之前的最新版本为6.0.新版本已经开始试行使用HTML5来管理vS ...
- [C++ Primer] 第7章: 类
定义抽象数据类型 定义在类内部的函数是隐式的inline函数. const成员函数 又叫做常量成员函数, 成员函数参数列表之后紧跟const关键字, const修饰的是类this指针. 默认情况下th ...
- PHP 设计模式 原型模式(Prototype)之深/浅拷贝
看PHP 设计模式 原型模式(Prototype)时,衍生出一个扩展问题之 原型拷贝的浅拷贝和深拷贝问题(不管写Java还是写PHP还是写JS时都多多少少遇到过对象拷贝问题) 比如写前端页面时 ...
- Appcan、apicloud、HBuilder 不同之处解析
来源:http://www.mamicode.com/info-detail-1129829.html 现在Hybrid app是一中非常火热的开发模式,在国内对应的开发工具也乱象丛生,有WeX5.c ...
- Bootstrap学习之路(1)---开篇-登陆页
Bootstrap是现在很流行的一套前端框架,尤其是它的自适应,真的很不错,而且对移动设备也很友好,可以达到快速开发的效果,最近想把自己的网站弄个手机版,很果断的就选用了bootstrap,跟大家分享 ...
- win7 php5.6 redis扩展
步骤: 1.下载redis扩展 redis扩展下载地址:http://windows.php.net/downloads/pecl/snaps/redis/ 查看phpinfo下载匹配的版本(一定要选 ...
- Java利用ScriptEngineManager对计算公式的支持
1.ScriptEngineManager是JDK6提出的相关方法,这方式的主要目的就是用来对脚本语言的处理.这里只是简单介绍一下对我们常用的数学公式的应用. 2.ScriptEngineManage ...
- DSL与编译原理
DSL:领域语言 类似于SQL的一种语言,比如自创一种语言,如何解析 可以自己实现类似于一种语言: 比如hibernate里面的sql解析就使用ANTLR 比如:http://hellojinjie. ...
- PDF预览之PDFObject.js总结
get from:PDF预览之PDFObject.js总结 PDFObject.js - 将PDF嵌入到一个div内,而不是占据整个页面(要求浏览器支持显示PDF,不支持,可配置PDF.js来实现 ...