JMM不避免指令重排,而是通过happens-before规则和内存屏障限制重排:volatile禁止读写重排并保证可见性,synchronized依托锁机制确保临界区有序,final字段保障构造安全,原子类与Lock依赖底层屏障实现有序性。

Java 内存模型(JMM)本身不“避免”指令重排,而是通过定义明确的规则(尤其是 happens-before 和内存屏障机制),限制重排的范围,确保关键操作的有序性与可见性。真正起作用的是 JMM 规则在 JVM 和硬件层面的落地实现——比如 volatile、synchronized、final 字段语义、以及显式锁和原子类等同步工具。
volatile:最轻量但关键的有序性保障
volatile 变量的写操作后插入写屏障(StoreStore + StoreLoad),读操作前插入读屏障(LoadLoad + LoadStore)。这能禁止两类重排:
- 禁止 volatile 写与其后的任何读/写操作重排(防止后置操作“提前”)
- 禁止 volatile 读与其前面的任何读/写操作重排(防止前置操作“滞后”)
典型场景:双重检查锁定(DCL)单例中,必须用 volatile 修饰 instance 字段。否则 new Singleton() 的三步(分配内存 → 初始化对象 → 赋值引用)可能被重排为“分配内存 → 赋值引用 → 初始化对象”,导致其他线程拿到未初始化完成的对象引用。
synchronized 与锁机制:基于 happens-before 的强顺序约束
进入 synchronized 块前的写操作,对退出该块后其他线程的读操作是可见的;且解锁操作 happens-before 后续对该锁的加锁操作。这种语义天然抑制跨临界区的重排:
立即学习“Java免费学习笔记(深入)”;
- JVM 在 monitorenter/monitorexit 插入内存屏障,强制刷新工作内存到主存、清空本地缓存
- 保证临界区内所有操作不会被重排到锁外,也不会让锁外操作“挤进”临界区
final 字段:构造安全的有序性基石
被 final 修饰的字段,在构造器内完成赋值后,其值对其他线程可见——前提是对象本身正确发布(如不发生逸出)。JMM 保证:final 字段的写操作不会被重排到构造器外,且对象引用的发布操作不会被重排到 final 字段赋值之前。这是实现不可变对象和安全发布的基础。
显式同步工具:原子类与 Lock 的底层屏障支持
java.util.concurrent.atomic 包中的原子类(如 AtomicInteger)、ReentrantLock 等,内部均依赖 Unsafe 类的 volatile 读写及 CAS 操作,这些操作在 JVM 层会生成带内存屏障的 CPU 指令(如 x86 的 lock xadd 或 mfence),从而在读-改-写过程中同时保障可见性与有序性,避免因重排导致状态判断失效或更新丢失。


















