浅谈Java多线程(synchronized实现可见性)
2016-08-12 14:07
357 查看
Java语言层面支持可见性的实现方式有synchronized和volatile两种。
synchronized能实现原子性(同步)和可见性。
JMM关于synchronized的两条规定:
1.线程解锁前,必须把共享变量的最新值刷新到主内存中;
2.线程加锁时,先清空工作内存中共享变量的值,因此使用共享变量时需要从主内存重新读取最新值。
注:加锁和解锁需要同一把锁
补充几个知识点
重排序:代码书写的顺序与实际执行顺序不同,编译器/处理器为了提高程序性能而做的优化。
as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致。(Java编译器、运行时和处理器都保证Java在单线程下遵循as-if-serial语义)
多线程中程序交错执行时,重排序可能造成内存可见性问题。
来一个实例,我们一起分析。
public class SynchronizedDemo {
// 共享变量
private boolean ready = false;
private int result = 0;
private int number = 1;
// 写操作
public void write() {
ready = true; // 1.1
number = 2; // 1.2
}
// 读操作
public void read() {
if (ready) { // 2.1
result = number * 3; // 2.2
}
System.out.println("result的值为:" + result);
}
// 内部线程类
private class ReadWriteThread extends Thread {
// 根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
private boolean flag;
public ReadWriteThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
// 构造方法中传入true,执行写操作
write();
} else {
// 构造方法中传入false,执行读操作
read();
}
}
}
public static void main(String[] args) {
SynchronizedDemo synDemo = new SynchronizedDemo();
// 启动线程执行写操作
synDemo.new ReadWriteThread(true).start();
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// 启动线程执行读操作
synDemo.new ReadWriteThread(false).start();
}
}
针对关键代码分析执行顺序:
执行顺序可以是:
1.1-2.1-2.2-1.2,result:3
1.2-2.1-2.2-1.1,result:0(重排序)
···
导致共享变量在线程间不可见的原因:
1.线程交叉执行;
2.重排序+线程交叉执行;
3.共享变量更新后的值没有在工作内存和主内存中及时更新。
安全代码:
// 写操作
public synchronized void write() {
ready = true; // 1.1
number = 2; // 1.2
}
// 读操作
public synchronized void read() {
if (ready) { // 2.1
result = number * 3; // 2.2
}
System.out.println("result的值为:" + result);
}
synchronized能实现原子性(同步)和可见性。
JMM关于synchronized的两条规定:
1.线程解锁前,必须把共享变量的最新值刷新到主内存中;
2.线程加锁时,先清空工作内存中共享变量的值,因此使用共享变量时需要从主内存重新读取最新值。
注:加锁和解锁需要同一把锁
补充几个知识点
重排序:代码书写的顺序与实际执行顺序不同,编译器/处理器为了提高程序性能而做的优化。
as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致。(Java编译器、运行时和处理器都保证Java在单线程下遵循as-if-serial语义)
多线程中程序交错执行时,重排序可能造成内存可见性问题。
来一个实例,我们一起分析。
public class SynchronizedDemo {
// 共享变量
private boolean ready = false;
private int result = 0;
private int number = 1;
// 写操作
public void write() {
ready = true; // 1.1
number = 2; // 1.2
}
// 读操作
public void read() {
if (ready) { // 2.1
result = number * 3; // 2.2
}
System.out.println("result的值为:" + result);
}
// 内部线程类
private class ReadWriteThread extends Thread {
// 根据构造方法中传入的flag参数,确定线程执行读操作还是写操作
private boolean flag;
public ReadWriteThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
// 构造方法中传入true,执行写操作
write();
} else {
// 构造方法中传入false,执行读操作
read();
}
}
}
public static void main(String[] args) {
SynchronizedDemo synDemo = new SynchronizedDemo();
// 启动线程执行写操作
synDemo.new ReadWriteThread(true).start();
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// 启动线程执行读操作
synDemo.new ReadWriteThread(false).start();
}
}
针对关键代码分析执行顺序:
执行顺序可以是:
1.1-2.1-2.2-1.2,result:3
1.2-2.1-2.2-1.1,result:0(重排序)
···
导致共享变量在线程间不可见的原因:
1.线程交叉执行;
2.重排序+线程交叉执行;
3.共享变量更新后的值没有在工作内存和主内存中及时更新。
安全代码:
// 写操作
public synchronized void write() {
ready = true; // 1.1
number = 2; // 1.2
}
// 读操作
public synchronized void read() {
if (ready) { // 2.1
result = number * 3; // 2.2
}
System.out.println("result的值为:" + result);
}
相关文章推荐
- java多线程---顺序打印ABC的三种实现---synchronized方式
- java多线程之内存可见性-synchronized、volatile
- Java多线程的~~~synchronized加入参数,以实现独立片段
- Java多线程之内存可见性和原子性:Synchronized和Volatile的比较
- java多线程---等待/唤醒以及生产者消费者经典同步synchronized的实现
- java多线程之内存可见性之synchronized
- java线程-synchronized实现可见性代码
- Java之多线程内存可见性_2(synchronized实现可见性代码)
- synchronized实现可见性过程
- Java同步机制浅谈――synchronized对代码作何影响?
- 浅谈权限管理的对象模型和实现
- Java同步机制浅谈――synchronized对代码作何影响? (转载)
- 浅谈MFC中超类化技术的实现
- 浅谈如何利用PB实现动态添加菜单(原创)
- 浅谈即时战略游戏在 J2ME 上的实现
- Java同步机制浅谈―synchronized
- Java同步机制浅谈――synchronized对代码作何影响?
- 关于用VFP实现网页主界面的浅谈
- 浅谈集群情况下的session实现机制
- 浅谈Windows 2000/XP File Cache实现(http://webcrazy.yeah.net)