Java中volatile的可见性依赖CPU硬件MESI协议与内存屏障协同实现:写操作通过lock指令触发缓存行状态升为M并广播RFO使他核置I,读操作因缓存失效强制远程加载最新值,且配合LoadLoad/LoadStore屏障防止重排序。

Java 中 volatile 的可见性不是靠 JVM 自己“造出来”的,而是借用了 CPU 硬件已有的能力——MESI 协议和总线嗅探机制。它不写数据到磁盘,也不直接控制主内存刷写时机,而是通过触发缓存状态变更,让其他 CPU 核心“不得不”看到最新值。
volatile 写操作如何激活 MESI 状态流转
当一个线程写 volatile 变量时,JVM 会生成带 lock 前缀 的汇编指令(比如 lock xchg 或 lock addl $0, (%rsp)),这会强制触发两个关键动作:
- CPU 将该变量所在缓存行的状态从 E(独享)或 S(共享)升级为 M(已修改),标记为“脏”
- 向总线广播 RFO(Read For Ownership)请求,要求其他核心把同一地址的缓存行全部置为 I(Invalid)
其他核心怎么“被迫看到新值”
另一个线程读这个 volatile 变量时,CPU 检查本地缓存行状态:
- 若状态是 I(无效),必须发起远程读(remote read),从主内存或拥有 M 状态的核缓存中加载最新值
- 这个加载过程天然绕过 Store Buffer,避免因写缓冲延迟导致的不可见问题
- 同时,JVM 在读操作前后插入 LoadLoad 和 LoadStore 内存屏障,防止后续指令被重排到读之前
为什么普通变量做不到这点
普通变量的读写只是更新本地缓存,不会触发 RFO 或 invalidate 广播:
立即学习“Java免费学习笔记(深入)”;
- 缓存行可能长期停留在 S 或 E 状态,其他核仍用旧副本,哪怕主内存已被更新
- 没有内存屏障,编译器和 CPU 都可能重排序,进一步破坏观察顺序
- Store Buffer 中的写入可能迟迟不刷新,导致其他核永远看不到变化
内存屏障与 MESI 是协同工作的
volatile 不是只靠 MESI,也不是只靠内存屏障,而是两者配合:
- 写 volatile 时插入 StoreLoad 屏障,强制本核 Store Buffer 刷空,让 M 状态缓存行更快进入可同步队列
- 读 volatile 时插入 LoadLoad 和 LoadStore 屏障,确保读取动作不被重排,且后续读写依赖此值
- MESI 负责跨核数据一致性,内存屏障负责单核指令执行顺序,共同满足 Java 内存模型(JMM)对可见性的定义


















