判断并发安全性关键在于是否存在可靠的happens-before关系:只要两个有数据依赖的操作能通过程序次序、volatile变量、线程启动/终止等8条规则之一或链式推导出顺序性,即可保证可见性与有序性,无需显式加锁。

判断并发安全性,关键不是“有没有加锁”,而是“操作之间是否存在可靠的 happens-before 关系”。只要两个有数据依赖的操作(比如写后读)能通过 8 条先行发生原则中的任意一条或几条链式推导出顺序性,就能保证可见性和有序性,无需显式加锁。
程序次序规则:单线程内的天然保障
同一线程中,控制流上先执行的语句,其结果必然对后续语句可见。比如:
-
// 线程 A 内
int x = 42;
boolean flag = true;
这里 x = 42 happens-before flag = true,因为它们在同一线程、按控制流顺序执行。即使 JVM 重排序了指令,也不会破坏这个语义——这是 JMM 对单线程 as-if-serial 的承诺。
volatile 变量规则:轻量级的跨线程同步
对 volatile 字段的写,happens-before 后续任意线程对该字段的读。它不阻止重排序,但插入内存屏障,确保写值立即刷入主存、读值强制从主存加载。
-
// 线程 A
ready = false;
data = 100;
ready = true; // volatile 写 -
// 线程 B
if (ready) { // volatile 读
System.out.println(data); // data 的值一定能看到 100
}
因为 ready = true happens-before if(ready),而 volatile 写读的传递性又保证了 data = 100 的写对线程 B 可见(前提是写在 volatile 写之前,且无其他干扰)。
线程启动与终止规则:生命周期自带顺序
调用 thread.start() 就建立了主线程与子线程之间的 happens-before 链;而 thread.join() 返回,则意味着子线程所有操作都 happens-before 主线程后续动作。
- 子线程内修改共享变量 →
join()返回 → 主线程可安全读取该变量,无需额外同步 - 适用于一次性初始化场景,例如后台加载配置后通知主线程使用
锁规则与传递性:组合推导更灵活
虽然题目要求“不使用显式锁”,但理解锁规则有助于识别隐式同步点。更重要的是,happens-before 具有传递性:A → B 且 B → C ⇒ A → C。
- 线程 A 释放锁 L → 线程 B 获取锁 L(满足管程锁定规则)
- 线程 B 写 volatile 变量 v → 线程 C 读 v(满足 volatile 规则)
- 那么线程 A 的写操作,间接通过 A→B→C 链,对线程 C 可见
这种链式推导是实际编码中规避锁、保障安全的核心技巧,尤其适合分阶段协作的并发流程。










