不加volatile,线程不可见的问题,与理论的结果有出入?
下面这段代码是比较常见的证明线程之间不可见的例子,但是我想问的是如果将 Thread.sleep(2000);这段代码去了,为什么第一个线程的死循环就能够跳出,线程之间不是不可见的吗?
我猜测的两种答案:
1.后一个线程先执行结束了,initFlag的共享变量副本进入主内存了,第一个线程再执行。但是我将步骤打印出来,结果并不是这样。
2.之前查阅了其他博客,说是System.out.println内置同步锁,触发了缓存一致性协议!
于是乎,我有将System.out.println换成logeer,但是还是没有出现理论上线程之间不可见的答案。这两种推论都不成立。
头发都急白了,大神help!help!谁能够说明白,为什么将Thread.sleep(2000)去了,没有加volatile,但是线程之间还是可见了?
/** * 线程之间是不可见的
*/
public class VolatileVisibilityTest {
private static boolean initFlag =false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
System.out.println("waiting.....data");
while (!initFlag){
}
System.out.println("wait....data....end");
}).start();
Thread.sleep(2000);
new Thread(()->predData()).start();
}
public static void predData(){
System.out.println("pred....data");
initFlag = true;
System.out.println("pred....data....end");
}
}
回答:
说一下我的猜测和分析,不确定对不对。感觉可能是编译成的汇编导致的,所以我打印了生成的汇编。使用jdk1.8测试,
在不加voliate关键字时,线程的run方法总共被编译了三次,我用jitwatch查看汇编。
这是第一次编辑出来的
这是第三次编译出来的
下边是加上voliate关键字后,最后一次编译出来的代码
我汇编也不是很好,我的理解是:在不加voliate关键字时,jvm觉得initflag变量是线程独有的,其值永远都是false,所以最后编译出来的那个循环中就没有判断initFlag,是一个死循环了。
加上voliate关键字时,每次循环才会去从内存读initflag。
至于Thread.sleep(2000)的作用。这句话确实关系很大。因为jvm的优化和代码运行时间运行次数有关。所以当你sleep 2秒是,可以让线程中的while循环跑很多次,从而触发了编译优化,最终生成的死循环。如果你不加sleep语句,最终生成的就是第一张图片的汇编。
以上是 不加volatile,线程不可见的问题,与理论的结果有出入? 的全部内容, 来源链接: utcz.com/p/944100.html