volatile写操作通过JVM插入StoreStore和StoreLoad屏障禁止重排序并刷新写缓冲区,CPU将缓存行标记为Modified状态、写入本地缓存,并广播Invalidate请求使其他核缓存行失效。

volatile 关键字本身不直接触发总线锁,它依赖硬件层面的缓存一致性协议(如 MESI)协同工作,再配合 JVM 插入的内存屏障指令,共同实现可见性和有序性。总线锁是早期解决缓存不一致的粗粒度方案,现代 CPU 已基本弃用;volatile 的实际生效机制,核心在 MESI 协议与内存屏障的组合。
volatile 写操作如何触发缓存行状态变更
当一个线程写 volatile 变量时:
- JVM 在该写指令前后插入StoreStore 和 StoreLoad 内存屏障,禁止重排序,并强制刷新写缓冲区
- CPU 将对应缓存行标记为 M(Modified) 状态,并把新值写入本地缓存
- 通过总线嗅探(Bus Snooping)广播该缓存行地址的“失效请求”(Invalidate Request)
- I(Invalid)
volatile 读操作如何保证看到最新值
当一个线程读 volatile 变量时:
- JVM 插入LoadLoad 和 LoadStore 内存屏障,确保后续读写不会被重排到该读之前
- CPU 检查本地缓存中对应缓存行的状态:若为 I(无效),则必须发起总线请求,从主存或其他 CPU 的 M/E 状态缓存行中获取最新值
- 若其他 CPU 当前持有该行的 M 状态,会先将其写回主存(或直传给请求方),再降级为 S;本 CPU 加载后状态变为 S
- 因此,每次读都能获得全局最新值——这就是“可见性”的硬件基础
为什么不是总线锁?
总线锁(LOCK# 信号)会冻结整条系统总线,阻塞所有 CPU 对内存和其他设备的访问,开销极大。而 volatile 依赖的是更精细的 MESI 协议:
立即学习“Java免费学习笔记(深入)”;
- 只对涉及的单个缓存行做状态同步,不影响其他数据访问
- 通过广播+嗅探实现异步响应,无须全局等待
- 现代 x86 架构中,volatile 写通常触发的是“缓存锁定”(Cache Locking),即仅锁定当前缓存行,而非总线
- JVM 规范未规定必须用总线锁,HotSpot 实际实现完全基于 MESI + 内存屏障
关键点小结
volatile 不是魔法,它的效果是软硬协同的结果:
- Java 层:定义语义(可见性、禁止重排序),JVM 编译时插入对应内存屏障
- CPU 层:内存屏障映射为特定指令(如 x86 的
lock addl $0,0(%rsp)或 mfence),触发缓存一致性协议行为 - 硬件层:MESI 协议自动维护多核间缓存行状态,确保 I 状态强制回源、M 状态主动通知失效
- 它不涉及锁对象、不阻塞线程调度、不提供原子性——这些都和 synchronized 或 CAS 有本质区别


















