测试代码
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在运行一段时间后就会终止。
上面的一大段空白,都是
print
输出空白行所致,最后运行结束。这是因为print
内部使用了syncronized
关键字,强制工作内存变量副本失效,重新从主内存读取最新值。java
体验AI代码助手
代码解读
复制代码
public void println() { newLine(); } public void println(boolean x) { synchronized (this) { print(x); newLine(); } }