volatile通过写屏障强制刷新主内存、读屏障强制重载最新值,并借助MESI协议与总线嗅探实现跨核缓存失效,从而逐变量保障可见性,不依赖锁或全局同步。

Java 中 JMM(Java 内存模型)的内存可见性,不是靠“自动同步”实现的,而是通过 volatile 对变量读写行为施加明确约束来保障的。它不依赖锁,也不刷新所有变量,只精准作用于被修饰的变量本身。
volatile 怎么强制变量“看得见”
JMM 规定:每个线程有自己的工作内存(对应 CPU 缓存),共享变量初始从主内存读入,后续读写都在工作内存中进行,再择机写回主内存。普通变量下,线程 A 修改后可能迟迟不刷回主内存,线程 B 一直读自己缓存里的旧值——这就断开了可见性链路。
volatile 打断这个链路断裂的过程:
- 写 volatile 变量时,JVM 会插入写屏障(Store Barrier),强制把当前线程工作内存中的最新值立即写入主内存;
- 读 volatile 变量时,JVM 插入读屏障(Load Barrier),要求线程必须放弃工作内存中的缓存副本,直接从主内存(或通过缓存一致性协议如 MESI 获取最新副本)重新加载值。
底层靠硬件协同完成“即时通知”
仅靠 JVM 插入屏障还不够——真正让“一个核改了,另一个核马上知道”,靠的是 CPU 级别的缓存一致性机制:
立即学习“Java免费学习笔记(深入)”;
- 当某个 CPU 核心写入 volatile 变量,会触发该缓存行(Cache Line)状态变为 “Modified”;
- MESI 协议通过总线嗅探(Bus Snooping)广播该变更,其他核心发现自己的对应缓存行是 “Shared” 或 “Invalid”,就立即将其置为 “Invalid”;
- 下次读取时,因缓存行失效,CPU 必须重新从主内存或其它核心的缓存中拉取最新值——这一步由硬件自动完成,volatile 让 JVM 有理由触发它。
它不保证“全局一致的快照”,只保证单变量读写的及时性
注意:volatile 不是给整个对象或多个变量做原子快照。比如两个 volatile 变量 a 和 b,线程 A 先写 a 再写 b,线程 B 读到 b 的新值,并不意味着一定能同时读到 a 的新值(除非用 happens-before 关系串起来)。它的可见性保障是逐变量、逐操作生效的,不是事务性的。
典型验证方式:死循环退出失败 → 加 volatile 后立刻响应
这是最直观的验证场景:
- 一个线程修改
flag = true; - 另一线程执行
while (!flag) { ... }; - 不加 volatile,worker 线程可能永远卡在循环里(JIT 甚至可能优化成死循环);
- 加上 volatile 后,只要 flag 被写入主内存,worker 下一次读就会命中最新值,循环立即退出。


















