Java内存模型通过happens-before关系而非时间先后保证可见性,volatile写-读构成happens-before链,确保读到volatile新值时能见到该写前所有内存操作;其底层依赖内存屏障,语义等价于锁的释放与获取,但不保证原子性或复合操作的线程安全。

Java 内存模型(JMM)不靠“时间先后”保证可见性,而是靠 happens-before 关系建立逻辑上的先后约束。其中 volatile 变量写规则 是最常用、最轻量的可见性保障机制之一。
volatile 写-读天然构成 happens-before 链
对一个 volatile 变量的写操作,happens-before 于后续任意线程对该变量的读操作。这意味着:只要线程 B 读到了线程 A 写入的 volatile 值,那么线程 A 在该写操作之前所做的所有内存操作(包括对其他普通变量的写),对线程 B 都是可见的。
- 这个“之前”不是指代码行号顺序,而是指在同一个线程内,按程序顺序排在 volatile 写之前的那些操作
- JVM 和 CPU 必须确保这些操作的结果,在 volatile 写生效前已刷新到主内存;而 volatile 读则必须从主内存重新加载,不能用缓存旧值
- 底层通过内存屏障(Memory Barrier)实现:写 volatile 插入 StoreStore + StoreLoad 屏障,读 volatile 插入 LoadLoad + LoadStore 屏障
它不是“让别人立刻看到”,而是“看到时一定看到全部”
volatile 不保证读操作“马上发生”,也不强制线程立即同步。它的关键语义是:一旦读到最新的 volatile 值,就一定能见到该写发生前的所有副作用。
- 例如:线程 A 执行
x = 42; flag = true;(flag 是 volatile),线程 B 检测到flag == true,此时 x 的值一定是 42,不会是 0 或未初始化状态 - 如果线程 B 还没读到 true,那它看到什么、什么时候看到,JMM 不承诺——但只要它读到了 true,x 的可见性就由 happens-before 链严格保证
- 这正是 volatile 能用于“状态标志+数据发布”的根本原因,比如初始化完成通知、配置加载完毕等场景
它和锁的语义本质一致,只是粒度更细
volatile 的写-读,与 synchronized 的 unlock-lock 具有相同的内存语义:
立即学习“Java免费学习笔记(深入)”;
- volatile 写 ≈ 锁释放:把本地修改强制刷出到主内存,并禁止其后的读写重排序到它前面
- volatile 读 ≈ 锁获取:从主内存重新加载数据,并禁止其前的读写重排序到它后面
- 所以 volatile 可以替代部分简单的锁场景(如只做状态通知、单次写后只读),避免阻塞和上下文切换开销
它不解决原子性,也不组合生效
volatile 只保障单个读或写的可见性与有序性,不扩展到复合操作:
-
counter++(读-改-写)即使 counter 是 volatile,依然存在竞态,因为三个步骤之间没有 happens-before 保护 - 多个 volatile 变量之间无隐含顺序:写 v1 和写 v2 之间若无其他 happens-before 边,它们的相对顺序对其他线程不可见
- 要保证一组操作的整体可见性,仍需借助锁、final 字段初始化,或显式插入其他 happens-before 边(如 Thread.start/join)


















