happens-before 是逻辑上的“必须先发生、结果才可见”约束关系,volatile 通过内存屏障实现其最轻量落地,保障可见性与有序性但不保证原子性。

happens-before 不是时间先后,而是逻辑上的“必须先发生、结果才可见”的约束关系。volatile 是它最轻量也最常用的落地方式之一。
volatile 写-读天然构成 happens-before 链
对一个 volatile 变量的写操作,happens-before 于后续任意线程对该变量的读操作。这个“后续”不指代码紧挨着,而指该读操作实际读到了这次写的值。
- 线程 A 执行
x = 42; flag = true;(flag 是 volatile) - 线程 B 检查到
flag == true,此时它一定能看见x == 42 - 关键不在“B 立刻看到 true”,而在“B 一旦看到 true,就一定连带看到 x=42 的全部副作用”
靠传递性把普通变量“带进来”
单靠 volatile 变量本身不能保证其他数据可见,但 happens-before 具有传递性,能把普通写操作“链”进可见范围。
- 程序次序规则:A 线程中
x = 42happens-beforeflag = true - volatile 规则:
flag = truehappens-before B 线程的if(flag) - 传递规则:所以
x = 42happens-before B 的读判断 → x 对 B 可见
底层靠内存屏障,不是靠“刷缓存”
volatile 不是让 CPU 主动去同步,而是通过插入内存屏障(Memory Barrier)来约束重排序和访问路径:
立即学习“Java免费学习笔记(深入)”;
- volatile 写后加 StoreStore + StoreLoad 屏障:确保前面所有写操作完成并刷新,且禁止后面读写乱序到它前面
- volatile 读后加 LoadLoad + LoadStore 屏障:确保本次读从主内存加载,且禁止前面读写乱序到它后面
- 效果上等价于 synchronized 的 unlock-lock,只是粒度更细、无锁开销
它不解决所有问题,边界要清楚
volatile 能保可见性和有序性,但不保原子性,也不保复合操作的线程安全:
-
counter++(读-改-写)即使 counter 是 volatile,仍可能丢失更新 -
if (flag) doSomething();中,flag 可见不代表 doSomething() 执行过程受保护 - 只适合“状态标志 + 数据发布”这类简单协作场景,比如初始化完成、配置生效、开关切换


















