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

现代JIT编译器采用何种策略避免破坏自旋锁?

Java并发可见性问题与JIT编译器自旋锁优化策略

先看《Java并发编程实战》中的经典示例代码:

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    } 
}

这段代码可能出现两类错误:

  • ReaderThread输出0;
  • ReaderThread陷入无限循环。

错误原因分析

处理器不会修改缓存一致性协议的延迟速度,因此volatile关键字的核心作用是禁止指令重排序:

  • 输出0是由于指令重排序导致的,主线程中number = 42和ready = true的执行顺序被打乱,ReaderThread先读取到ready = true,但此时number还未赋值为42;
  • 无限循环并非缓存一致性问题导致,而是JIT编译器重排序与处理器运行时重排序共同作用的结果。

JIT编译器和处理器遵循volatile非重排序语义的核心是:保持volatile指令的位置固定,其上下的指令可重排、省略或消除,但不会跨越volatile指令。

ReaderThread出现无限循环的根源在于JIT的单线程优化:如果自旋锁内加载的变量在当前线程生命周期内没有被修改的迹象,JIT会将该变量提升到循环外(缓存到寄存器中),导致线程无法读取到其他线程对变量的修改。这是因为线程内一致性规则让JIT仅关注单线程的程序执行顺序(Program Order),无法感知其他线程的修改操作。

在VarHandle相关实践中,解决方案是在自旋内使用getOpaque方法——getOpaque本身没有特殊语义,仅为兼容旧版JIT,让其将该操作视为volatile,从而避免破坏自旋锁的逻辑。

对现代JIT编译器策略的推测

基于上述分析,推测现代JIT编译器可能采用以下两种策略避免破坏自旋锁:

  1. 在线程内重排序前,对整个程序进行全局分析,识别变量是否可能被其他线程修改;
  2. 对自旋锁内的加载指令进行特殊的重新组织或重排序处理。

另外,C/C++中未出现此类问题,而C#存在类似问题,由此推测处理器重排序本身不会破坏自旋锁。

技术问询

现代JIT编译器具体采用何种策略避免破坏自旋锁?若上述理解存在错误,欢迎指正。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 18:54:54