《java并发编程实战》读书笔记一 可见性和重排序现象

时间:2022-03-17 17:59:52

第3.1章节《可见性》里给了一段代码:

publci class NoVisibility{
private static voolean ready;
private static int number;
private static class ReaderThread extends Thread{
public void run() {
while(!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}

根据作者意思,NoVisibility可能会输出42,也有可能输出0,甚至可能导致死循环,不会输出。原因是因为没有使用恰当的同步机制,没能保证主线程写入ready和number的值对读线程是可见的。
作者在书中提到两个概念:可见性和“重排序(reordering)”现象。

具体分析:
首先类加载会初始化ready和number的值分别为false和0,我们认为正常情况下,number被赋值为42,ready赋值为true,结果应该为:输出42,读线程、主线程执行完成,结束。
其实不然,还会有另外三种情况:

1.无限循环,number的值为0:在主线程即main方法中对ready的设置(即ready = true)还没来得及写回主存(静态变量保存在方 法区),ReaderThread 线程就已经读取了ready的值(并保留了副本),然后加载到Java栈中,此时ready 一直为false所以出 现死循环。number的值也可以类似推理,在主线程即main方法中对number的设置(即number= 42)还没来得及写回主存(静态变量保存 在方法区),ReaderThread 线程就已经读取了number的值(并保留了副本),然后加载到Java栈中,此时number一直为0(只是没 有打印出来而已);

2.无限循环,number的值为42:在主线程即main方法中对ready的设置(即ready = true)还没来得及写回主存(静态变量保存在 方法区),ReaderThread 线程就已经读取了ready的值(并保留了副本),然后加载到Java栈中,此时ready 一直为false所以 出现死循环。在主线程即main方法中对number的设置(即number= 42)后(即number的值已经写回了主 存),ReaderThread 线程才开始执行此时读取的number为42(只是没有打印出来而已);

3.输出0:在主线程即main方法中对ready的设置(即ready = true)后(即ready的值已经写回了主存),还没来得及写回主存(静 态变量保存在方法区),ReaderThread 线程就已经读取了number的值(并保留了副本),然后加载到Java栈中,此时number为0;

至于为什么会出现ready = true写回主存后,number = 42还没写回主存。这应该是由于Java虚拟机的一种优化技术叫指令重排序,number = 42不一定会在ready = true前面执行,得看Java虚拟机是怎么优化的。

内容来源:http://bbs.csdn.net/topics/390715765/