happens-before是Java内存模型中保证跨线程操作可见性与有序性的核心偏序关系;它不依赖时间先后,而是通过程序次序、volatile读写、线程启动/终止等8条规则或其链式传递,确保一个操作的结果对另一操作可见,从而实现无锁安全编程。

Java 中 happens-before 规则是编写无锁安全代码的底层逻辑依据。它不依赖 synchronized 或 Lock,而是通过明确的操作顺序约束,保证一个线程的写操作对另一个线程的读操作可见——只要能建立可靠的 happens-before 链,就无需加锁。
用程序次序规则保障单线程内操作顺序
这是最基础也最容易被忽略的一条。同一根执行流中,前面的语句天然 happens-before 后面的语句,JVM 不会破坏这种逻辑顺序(as-if-serial 语义)。
- 比如在初始化阶段:data = 42; ready = true; —— 只要这两句在同一线程中按此顺序执行,data 的赋值就一定对后续读 ready 的线程“可见”,前提是 ready 是 volatile
- 但注意:这个规则只在单线程内有效,不能跨线程传递;若想让其他线程看到 data=42,必须借助 volatile、start()、join() 等跨线程规则衔接
靠 volatile 变量规则构建轻量级同步点
volatile 写操作 happens-before 后续任意线程对该变量的读操作。这不是“禁止重排序”的保证,而是插入内存屏障,强制刷出写值、强制加载新值。
- 典型用法是“状态标志 + 数据准备”模式:先写数据,再写 volatile 标志;读线程先读标志为 true,再读数据——只要标志读到了,数据就一定是最新的
- 关键细节:数据写操作必须在 volatile 写之前完成(不能被重排序到后面),且读线程必须在读到 volatile 值之后才去读数据,否则链路断裂
- volatile 不能保证复合操作原子性(如 i++),但它能确保单次读/写的可见性和有序性
借线程启动与终止规则实现一次性协作
Thread.start() 建立主线程与子线程之间的 happens-before 关系;Thread.join() 返回,则子线程所有操作都 happens-before 主线程后续动作。
立即学习“Java免费学习笔记(深入)”;
- 适合场景:后台预加载资源、异步初始化配置、计算型任务结果回传
- 例如:子线程设置 result = compute(); done = true; 主线程调用 t.join(); 就能安全读 result,无需额外同步
- 注意:join() 是阻塞操作,适用于“等待完成”而非高频交互;它和 volatile 配合,可替代部分 Future 场景
用传递性把多个规则串成完整链路
Happens-before 具有传递性:A → B 且 B → C ⇒ A → C。这是无锁设计中最实用的技巧——把不同线程、不同机制的操作用中间节点连起来。
- 例如:线程 A 写 data=100 后 unlock 锁 L;线程 B 获取锁 L 后写 volatile flag=true;线程 C 读 flag 为 true 后读 data → data=100 对 C 可见
- 再如:主线程 start() 子线程 → 子线程内写 volatile config;主线程 later 读 config → 因为 start() → 子线程操作 → volatile 写 → 主线程 volatile 读,整条链成立
- 传递性让规则组合变得灵活,但每一步都必须严格满足某一条原始规则,不能靠“感觉”补链


















