jmm通过happens-before规则保障有序性:volatile写-读、synchronized锁释放-获取、程序顺序+传递性、final字段初始化均建立happens-before关系,确保操作可见性与禁止有害重排序。

Java 内存模型(JMM)本身不直接提供“有序性规则”这个术语,真正起作用的是 happens-before 规则——它不是强制执行的指令顺序,而是定义操作之间可见性与执行顺序约束的逻辑关系。所谓“避免并发 Bug”,核心是利用 happens-before 来保证:如果 A happens-before B,那么 A 的结果对 B 是可见的,且 JVM 和硬件不会做破坏该关系的重排序。
要靠有序性(更准确说是禁止有害重排序)来避坑,关键不是写“顺序代码”,而是用 JMM 提供的同步机制建立 happens-before 关系。以下是实际可用的方式:
用 volatile 建立写-读之间的 happens-before
volatile 不仅解决可见性,还禁止特定重排序:
- 写 volatile 变量前的所有操作,不能被重排到写之后;
- 读 volatile 变量后的所有操作,不能被重排到读之前。
例如:private int data = 0; private volatile boolean ready = false;
// 线程 A data = 42; // 普通写 ready = true; // volatile 写 → 此前所有操作(含 data=42)对后续读 ready 的线程可见
// 线程 B if (ready) { // volatile 读 System.out.println(data); // 能看到 42,不是 0 —— 因为 data=42 happens-before ready=true, // 且 ready=true happens-before ready==true,传递后 data=42 happens-before 这行读取 }
<strong>用 synchronized 锁定临界区,天然满足锁规则</strong>
synchronized 的解锁操作 happens-before 后续同锁的加锁操作。这意味着:
- 退出 synchronized 块前对共享变量的修改,一定对下一个获得该锁的线程可见;
- 编译器和 CPU 不会对块内操作做跨锁边界的重排序。
即使你只用锁来“保护读”,只要锁对象一致,就能建立 happens-before 链。
<strong>依靠程序顺序规则 + 传递性组合推导</strong>
单线程内,代码顺序即 happens-before 顺序(如 `x=1; y=2;` → `x=1` happens-before `y=2`)。配合 volatile 规则或锁规则,可串联出跨线程的保障。比如:
- 线程 A:`a = 1; flag = true;`(flag 是 volatile)
- 线程 B:`if (flag) { use(a); }`
→ `a=1` happens-before `flag=true`(程序顺序),`flag=true` happens-before `flag==true`(volatile 规则),再由传递性得 `a=1` happens-before `use(a)`。
<strong>final 字段的初始化安全(构造器内赋值)</strong>
对象构造完成时,对 final 字段的写入,happens-before 任何线程通过该引用读取该对象。这能防止“对象逸出”导致的字段未初始化问题,属于编译期+运行期联合保障的有序性约束。
不依赖这些机制,仅靠“代码写在前面”无法保证多线程下的执行顺序。JVM 和 CPU 会优化,而 happens-before 是唯一被 JMM 明确定义、被所有 JVM 实现必须遵守的语义契约。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











