Java中未被使用的volatile读操作是否可被优化?是否破坏先行发生关系?
问题解答:未被使用的volatile读操作为何无法保证可见性?
这不是编译器或JVM的Bug,完全是Java语言规范(JLS)允许的优化行为。
核心原因:无副作用的volatile读会被优化消除
JLS 17.4.5中定义的先行发生(happens-before)关系,依赖于实际执行的volatile读操作来传递可见性。但如果编译器或JVM能够证明某段volatile读操作没有任何可观测的副作用——比如你提到的把读操作放在空if语句里,不管读出来的结果是true还是false,都不会触发任何实际逻辑——那么就有权将这个读操作直接优化掉。
一旦这个volatile读被消除,原本期望通过它建立的先行发生链就断裂了:主线程对非volatile变量running的修改,依然无法保证被子线程的循环读取到,程序自然无法终止。
验证示例与反例
比如你可能写出的测试代码是这样的(优化前):
private static boolean running = true; private static volatile boolean running2 = true; public static void main(String[] args) throws InterruptedException { new Thread() { @Override public void run() { while (running) { if (running2) { // 空语句,无任何实际操作 } } } }.start(); Thread.sleep(1000); running = false; }
这段代码里的if(running2)没有任何可观测行为,JVM会直接删除这个volatile读操作,子线程的循环等价于只读取非volatile的running,所以看不到主线程的更新。
如果要让volatile读起到可见性传递的作用,你需要让这个读操作产生可观测的影响,哪怕是一个无意义的操作:
while (running) { // 用变量接收volatile读的结果,让JVM无法消除这个读 boolean temp = running2; }
或者执行实际逻辑:
while (running) { if (running2) { // 哪怕是个空的打印,只要有可观测行为就行 System.out.println(); } }
这时候JVM无法证明这个volatile读是无意义的,会保留读操作,先行发生关系得以建立,running的更新就能被子线程看到,程序可以正常终止。
内容的提问来源于stack exchange,提问作者stonar96
相关产品推荐
相关产品推荐

