You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何该JVM编译器优化合法?——《Effective Java》并发代码疑问

为什么JVM会对无同步的循环做这种看似改变语义的优化?

我正在阅读Joshua Bloch所著的《Effective Java》一书,作者在并发章节展示了如下代码片段:

public class StopThread {
  private static boolean stopRequested;

  public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
      int i = 0;
      while (!stopRequested) {
        i++;
      }
    });
    backgroundThread.start();
    
    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
  }
}

随后作者提到:

在没有同步的情况下,虚拟机完全可以将代码:

while (!stopRequested)
  i++;

转换为:

if (!stopRequested)
  while (true)
    i++;

对此我感到震惊,这完全改变了代码的语义,二者并不等价,为何虚拟机可以进行这种优化(甚至称其为优化都不合理)?能否有人为我解释?


  • 这是基于JVM的「单线程语义一致性」假设
    JVM的优化器默认遵循「as-if-serial」规则:只要优化后的代码在单线程环境下的执行结果和原代码完全一致,就允许进行优化。在单线程中,stopRequested的值不会在循环过程中被修改(无同步时JVM不会考虑其他线程的操作),所以把循环条件的检查移到循环外,执行结果和原代码没有区别——单线程里要么一开始就不进循环,要么一直循环下去。

  • 核心原因是「可见性」未被保证
    在多线程无同步的场景下,JVM没有义务保证一个线程对stopRequested的修改能被另一个线程立刻看到。后台线程可能会把stopRequested的值缓存到自己的工作内存(CPU寄存器或L1缓存)里,而非每次循环都去主内存读取。这种缓存优化能大幅提升性能——避免每次循环都做一次昂贵的主内存访问。

  • 这种优化是合理的(从JVM性能角度)
    单线程程序是绝大多数程序的常态,JVM的优化策略优先服务于单线程性能。对于没有显式同步(比如synchronized、volatile)的代码,JVM默认认为不存在跨线程的内存可见性需求,所以会大胆做这类缓存和重排序优化。只有当你显式声明了同步需求(比如给stopRequested加上volatile修饰符),JVM才会禁止这种优化,保证每次读取都从主内存获取最新值。

  • 为什么看起来语义改变了?
    你觉得语义改变是站在多线程视角,但JVM的优化是基于单线程语义合法的前提。在没有同步的多线程场景下,Java内存模型(JMM)本身就不保证线程间的内存可见性,原代码的「后台线程能看到主线程修改的stopRequested」这个行为其实是没有保障的——JVM允许后台线程永远看不到这个修改,所以把循环改成「一次检查后无限循环」,只是把这种未定义行为明确表现出来了而已。

内容的提问来源于stack exchange,提问作者morpheus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 18:21:09