happens-before 是 JMM 定义的偏序关系,用于保证操作可见性与顺序性;其实际约束由 JVM 插入的内存屏障(如 volatile 写插入 StoreStore+StoreLoad)落地执行。

Java 中 happens-before 机制本身不直接“执行”任何动作,它是一种逻辑上的偏序关系,用于定义操作之间的可见性与顺序约束。真正落地执行这些约束的,是 JVM 在编译和运行时插入的内存屏障(Memory Barrier)——它们是连接抽象语义与硬件行为的关键桥梁。
happens-before 如何映射为内存屏障
JVM 根据代码中建立 happens-before 关系的同步原语(如 volatile、synchronized、Thread.start 等),在对应位置插入特定类型的内存屏障指令。这些屏障限制编译器重排序,并向 CPU 发出信号,控制缓存、写缓冲区和读取路径的行为。
- 每个 happens-before 规则都对应一组屏障组合,不是凭空生效,而是由屏障强制保障
- 屏障本身不保证“立即刷新”或“马上读到”,而是确保:若某操作 A happens-before B,则 A 的写结果对 B 可见,且 A 和 B 之间不会因重排序而破坏该可见性
- 不同硬件平台对屏障的实现不同(如 x86 上 StoreLoad 对应 mfence,ARM 上用 dmb),JVM 的 OrderAccess 层负责适配
volatile 写/读背后的屏障组合
这是最典型的 happens-before 实现案例:对 volatile 变量的写操作,必须对后续读该变量的操作可见。
-
volatile 写操作 插入 StoreStore + StoreLoad 屏障
→ StoreStore:确保前面所有普通写已落主存,再执行 volatile 写
→ StoreLoad:阻止 volatile 写之后的读操作被提前到它前面(防止读到过期值) -
volatile 读操作 插入 LoadLoad + LoadStore 屏障
→ LoadLoad:确保前面所有读已完成,再读 volatile 变量
→ LoadStore:防止 volatile 读之后的写被重排到它前面(维持依赖顺序)
synchronized 块的屏障语义
进入和退出 synchronized 块分别建立锁规则的 happens-before 关系:
立即学习“Java免费学习笔记(深入)”;
-
进入同步块前:插入 LoadLoad + LoadStore 屏障
→ 保证能读到临界区外最新写入的共享变量(如其他线程释放锁时刷出的值) -
退出同步块时:插入 StoreStore + StoreLoad 屏障
→ 保证本线程在临界区内对共享变量的所有修改,对下一个获取该锁的线程可见
程序顺序规则的屏障隐含性
同一线程内,A 出现在 B 前,天然满足 A happens-before B。这看似“免费”,但底层仍需约束:
- 编译器不能把 B 重排到 A 前(除非无数据依赖),靠的是内部的指令调度限制,本质也是一种轻量级屏障语义
- JVM 会在方法入口、分支跳转、异常处理等关键点插入必要屏障,防止跨基本块重排序破坏单线程语义
- 这种顺序性不提供跨线程可见性,只有配合锁、volatile 或线程启动/终止等规则,才能形成线程间 happens-before 链


















