happens-before是JMM中定义的可见性契约,非时间先后而是逻辑先行关系;它保证A操作结果对B可见且B不看到中间态,由内存屏障与CPU指令协同实现。

你可以把 happens-before 想成 Java 并发世界里的“因果约定”——不是说谁在时间上先发生,而是说“如果 A happens-before B,那 B 一定能看到 A 做过的事”。它不保证物理时间顺序,但保证结果可见、逻辑可靠。
就像寄快递的签收单
假设你(线程A)打包好一个盒子(改了变量 x),写好一张纸条(flag = true),然后交给快递员(释放锁 / 写 volatile)。另一个人(线程B)只有拿到这张纸条(读到 flag == true),才能打开盒子看里面的东西(读 x)——而且他看到的一定是你装进去的最新版本。这个“拿到纸条 → 才能看盒子”的约定,就是 happens-before 在起作用。
日常能用上的四条核心规则
-
同一线程里,代码怎么写的,就怎么算先后:比如
a = 1; b = a + 2;,a 赋值一定 happens-before b 计算,不用加锁也成立 - 加锁解锁有接力关系:线程A退出 synchronized 块(解锁),线程B随后进入同一把锁的块(加锁),那么A在临界区里干的所有事,对B都可见
- volatile 变量是“广播喇叭”:谁写了 volatile 变量,后续所有读它的线程,都能立刻收到“最新播报”,包括它前面普通变量的修改(靠传递性)
-
start() 和 join() 是线程生命周期的锚点:主线程调用
t.start()后,t 里所有操作都“继承”了主线程此前的状态;t 执行完后,主线程调用t.join()就能确保看到 t 的全部结果
关键提醒:没 happens-before,就等于“没承诺”
如果两个操作之间没有任一规则覆盖(比如纯普通变量、无同步、不同线程、也没传递链),那 JVM 和 CPU 完全可能:
- 把你的赋值指令重排到后面去
- 让修改只留在自己 CPU 缓存里,不刷到主内存
- 让另一个线程一直读到旧值,甚至永远读不到
这时候不是程序“出 bug”,而是你没建立 happens-before 关系——相当于没签收货单,就指望别人一定收到货。
立即学习“Java免费学习笔记(深入)”;


















