先行发生原则解决多线程下的可见性与有序性问题;它通过程序次序、volatile变量、锁、线程启动和传递性等规则,构建无需同步器的默认内存契约,保障操作结果对后续操作可见且逻辑顺序可控。

“先行发生原则”不是时间上的先后,而是结果可见性与执行顺序的逻辑保障。它不关心代码在物理时钟上谁先谁后运行,只关心:如果操作 A happens-before 操作 B,那么 A 的执行结果(比如变量修改、消息发送、锁释放等)一定对 B 可见,且 B 能看到 A 完整的影响。
它解决的是什么问题?
多线程环境下,由于工作内存、主内存分离和指令重排序,一个线程改了变量,另一个线程可能看不到最新值——这就是可见性问题;也可能因为重排导致逻辑错乱——这就是有序性问题。happens-before 就是一套无需手动加锁也能默认成立的可见性契约,让程序员不用时刻盯着底层内存细节。
几条最常用的规则
这些规则共同构成 JMM 的“默认安全边界”,只要符合其中任意一条,就自动满足 happens-before 关系:
- 程序次序规则:单个线程内,按代码控制流顺序,前面的操作 happens-before 后面的操作(即使有分支或循环,也按实际执行路径算)
- volatile 变量规则:对 volatile 变量的写,happens-before 后续对该变量的读(注意:“后续”指该线程中时间上靠后的读,或其它线程中能观测到该写之后的读)
- 锁规则:解锁(unlock)操作 happens-before 后面对同一把锁的加锁(lock)操作(保证临界区退出后,新进入者能看到全部修改)
- 线程启动规则:Thread.start() 调用 happens-before 新线程里的任意操作
- 传递性:如果 A happens-before B,B happens-before C,那么 A happens-before C(这是推导复杂场景的关键)
特别注意两个常见误解
第一,happens-before 不等于时间先后:JVM 允许重排序,只要最终效果和按 happens-before 顺序执行一致即可。比如 A happens-before B,但 CPU 实际可能先执行 B 再执行 A——只要 B 看不到 A 未完成的状态,就不违规。
第二,没有规则覆盖的地方就没有保障:比如两个线程分别写不同变量、无锁无 volatile、也不跨线程通信,那它们之间就不存在 happens-before 关系,此时读写结果不可预测,就是数据竞争。
它本质是 Java 给开发者的一份轻量级“内存契约”,把底层复杂的重排、缓存、同步机制封装成几条清晰可记的逻辑规则。用好它,比盲目堆 synchronized 更接近问题本质。

















