您的位置:首页 > 编程语言 > Java开发

浅谈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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: