Java 多线程共享变量可见性研究

测试代码

 

java

体验AI代码助手

代码解读

复制代码

Thread thread1 = new Thread(new Runnable() { int i = 0; @Override public void run() { while (!flag){ i++; } } }); thread1.start(); flag = true;

上述代码是一直循环还是set flag = true之后立即结束?

答案是立即结束。

根据Java并行开发的经典理论,thread1会在其工作内存中保存一份flag值的副本,因此当主线程设置flag为true时,在未使用volatile修饰此变量的情况下,thread1的工作内存中flag暂时应该还是false,不会立即终止循环。然而在IDEA IDE中运行上述代码后,程序会立即结束,理论自然是正确的,为何会得出相悖的结果?可能有如下原因:

  • 主线程运行结束后整个程序会立即终止,但是当去掉最后一行代码后,程序还是在运行中,说明陷入了死循环,也证实了并非主线程运行结束了,所以整个程序才结束了。

  • 主线程结束后,将对flag的修改从其工作内存中刷新回了主内存。无法验证,何时把共享变量的值刷回主内存是一件不确定的事情。

  • 可能是JIT做了什么处理,让thread1每次读取flag时都从主内存中读取,看起来仿佛给此变量使用volatile修饰一样。不过笔者在使用Jitwatch+HSDIS在jdk17,MacOS平台下并未能看到相应的汇编代码,只能暂时作罢,等后面有空了再研究一下。

  • 最后一种可能最简单,在thread1的工作线程在从主内存读取flag变量值到工作内存之前,flag就已经被设置成true了,想验证也很简单,在10和11行代码之间加一个Thread.sleep(1000)即可,如下所示:

     

    java

    体验AI代码助手

    代码解读

    复制代码

    Thread thread1 = new Thread(new Runnable() { int i = 0; @Override public void run() { while (!flag){ i++; } } }); thread1.start(); Thread.sleep(1000); flag = true;

    上述代码会无限循环,看起来永远无法获取到flag的最新值。问题就出来了,即使不使用volatile修饰,现代计算机应该会在一个较短的时间内将修改值刷新回主内存,但现在看起来并不会,具体原因可以参考你了解的可见性可能是错的这篇文章,是JIT将代码“优化”成了一个无限循环代码。再稍微延伸一下,如何用最简单的方式让thread1使用flag的最新值,只需要加一个print语句即可。

     

    java

    体验AI代码助手

    代码解读

    复制代码

    Thread thread1 = new Thread(new Runnable() { int i = 0; @Override public void run() { while (!flag){ System.out.println(); } } }); thread1.start(); Thread.sleep(1000); flag = true;

    thread1在运行一段时间后就会终止。

    截屏2025-06-29 15.25.41.png

     上面的一大段空白,都是print输出空白行所致,最后运行结束。这是因为print内部使用了syncronized关键字,强制工作内存变量副本失效,重新从主内存读取最新值。

     

    java

    体验AI代码助手

    代码解读

    复制代码

    public void println() { newLine(); } public void println(boolean x) { synchronized (this) { print(x); newLine(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值