Happens-Before原则是JMM定义的逻辑偏序关系,用于保证多线程中操作的可见性与有序性:若A happens-before B,则A的结果对B可见且逻辑上先于B;它不依赖物理时间顺序,而是通过程序顺序、锁、volatile、启动、终止等规则建立跨线程可见性保障,并具有传递性。

Java 中 Happens-Before 原则是理解多线程可见性的核心钥匙。它不规定 JVM 实际怎么执行,而是定义一组“保证可见性与有序性”的逻辑约束——只要两个操作之间存在 Happens-Before 关系,前一个操作对共享变量的修改,就一定能被后一个操作看到。
可见性问题的本质是缓存与重排序
每个线程可能把变量缓存在自己的工作内存(如 CPU 缓存)中,而不是每次都读写主内存;编译器和处理器也可能为优化性能调整指令顺序。这导致:线程 A 修改了变量 x,线程 B 却读到旧值;或者 flag = true 写在 x = 42 后面,但线程 B 先看到 flag 为 true、却读到 x = 0。Happens-Before 就是用来打破这种不确定性,为程序员提供可推理的确定性边界。
关键规则直接对应常见同步手段
每条 Happens-Before 规则都把一种编程实践和内存语义绑定起来:
- volatile 变量规则:对 volatile 字段的写操作 happens-before 后续任意线程对该字段的读操作。这意味着写入立即刷回主内存,读取强制从主内存加载——无需加锁就能实现轻量级可见性保障。
- 监视器锁规则:线程释放锁(synchronized 块结束或 unlock)happens-before 其他线程获取同一把锁。因此,临界区内所有写操作的结果,在锁释放后必然对下一个获得该锁的线程可见。
- 线程启动与终止规则:主线程调用 thread.start() 之前的所有操作,happens-before 新线程的 run() 方法中任何操作;子线程所有操作 happens-before 主线程调用 thread.join() 返回。这是跨线程传递初始化状态或结果的基础。
传递性让组合使用成为可能
Happens-Before 具有传递性:如果 A → B,B → C,则 A → C。这个特性让复杂场景也能推导出可见性保障。例如:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 线程 A 在 synchronized 块中设置 x = 1 并释放锁;
- 线程 B 获取同一把锁后读到 x = 1,并在此后写 volatile flag = true;
- 线程 C 读到 flag = true,就能确保也看到 x = 1(因为 A → B → C,所以 A → C)。
这种链式推导,是避免过度加锁、灵活设计无锁/半锁结构的关键依据。
没有 Happens-Before 就没有可靠可见性
如果两个操作之间没有任何 Happens-Before 规则覆盖(比如普通变量的非同步读写),JVM 不做任何保证:可能看到新值、旧值、甚至部分更新的“撕裂”值。这不是 bug,而是 JMM 的明确设计——它把可见性责任交还给程序员,通过显式建立 HB 关系来换取性能。写并发代码时,每次访问共享变量,都应自问:这里有没有 HB 保障?如果没有,就需要补上 volatile、synchronized、Lock 或其他满足规则的机制。


















