Java 内存模型与 happens-before
JMM 与 happens-before:何时一次写入对其它线程可见,同步动作如何建立顺序。
#concept
#growing
#dev
#java
[!info] related notes
Java 内存模型与 happens-before
这个概念为什么出现
并发 bug 常不是“没想到交错”,而是没有同步却假设看见最新值。Java 内存模型(JMM)定义:在何种同步关系下,线程 A 的写对线程 B 的读可见且有序。核心工具概念是 happens-before。
[!abstract] 一句话理解 happens-before 约束哪些执行结果允许被观察,并不要求物理上禁止一切重排。常见关系包括同一锁的释放与后续获取、同一 volatile 的写与后续读,以及线程启动与结束检测。
最小可观察示例
场景:发布不可变配置快照
public class ConfigPublish {
static class Cfg {
final int port;
Cfg(int port) { this.port = port; }
}
private volatile Cfg ready; // 用 volatile 发布引用
void publish(int port) {
Cfg c = new Cfg(port); // 先构造完成
ready = c; // 发布:写 volatile
}
int readPort() {
Cfg c = ready; // 读 volatile
return c == null ? -1 : c.port;
}
public static void main(String[] args) throws Exception {
ConfigPublish p = new ConfigPublish();
Thread writer = new Thread(() -> p.publish(8080));
writer.start();
writer.join(); // join 建立 happens-before
System.out.println(p.readPort());
}
}
建议运行:
javac ConfigPublish.java && java ConfigPublish
期望输出:
8080
这个程序同时用了 volatile 与成功 join;即使去掉 volatile,主线程也会因 join 获得可见性,所以它不能单独证明 volatile 的必要性。配置还用了 final 字段,其初始化安全性另有规则。
结合场景再看三个关注点
- 安全发布对象构造完成后再发布引用。
- volatile/join/锁是规范层工具,不是“睡眠碰运气”。
- 没有同步的共享可变属于数据竞争,行为可极难推理。
核心概念与准确模型
1. happens-before 常见来源(直觉列表)
- 程序次序(单线程内)
- 监视器锁:unlock → 后续 lock 同一锁
- volatile:写 → 后续读同一变量
- 线程 start:start → 新线程中的动作
- 线程终止检测:线程全部动作 → 另一线程通过 join 成功确认终止后的动作;带超时的 join 若超时返回,不代表线程已结束。\
- 传递性
2. 数据竞争
无 happens-before 关系的冲突读写 = 数据竞争;正确程序应避免。
3. 规范 vs 硬件
- 用规范推理正确性
- CPU/缓存是实现与直觉辅助,不能替代规范
边界情况与反直觉行为
- 双重检查锁定必须正确使用 volatile(经典题)。
- 正确构造且构造期间不逸出 this 的对象,其 final 字段有专门的初始化安全保证;这不让所有可变字段自动线程安全。\
- 单测“偶尔通过”不能证明无数据竞争。
常见误区
[!warning] 常见误区:加几个 sleep 让并发测试变绿 那是掩盖竞态;应建立同步与用并发测试工具。
工程实践
- 优先不可变 + 消息传递缩小共享。
- 共享可变必须能指出 happens-before 边。
- 用 jdk.jfr / 线程转储 / 并发 TLA 级推理(进阶)。
- 读 JCIP 与 JLS/JMM 章节交叉验证。
本节总结
- JMM 回答可见性与有序
- happens-before 是推理货币
- 无同步共享可变即风险
自测题
- 为什么 unlock 对后续 lock 重要?
- join 提供什么保证?
参考答案
- 释放锁 happens-before 后续获取同一锁,约束后续读取能观察到的结果;“刷新缓存”只是实现类比。\
- 成功确认目标线程终止后,目标线程的动作对后续操作可见;超时返回或因中断抛异常不自动提供同一终止保证。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JLS Chapter 17 Threads and Locks | 规范 | JMM |
| JSR 133 FAQ | 经典 FAQ | 直觉问答 |