volatile保证可见性依赖CPU的MESI协议:写操作触发lock指令使缓存行变M态并广播作废其他核心副本,读操作遇I态则强制重载最新值,从而实现多核间即时可见。

volatile 保证可见性,不是靠 JVM 单独“加锁”或“刷内存”,而是借助底层 CPU 的 MESI 缓存一致性协议,配合 volatile 写操作触发的 lock 前缀指令,协同完成的。
MESI 是怎么让其他线程“立刻看到”修改的?
MESI 协议通过监控 CPU 缓存行(Cache Line)的状态变化,确保多个核心对同一变量的缓存副本保持同步。当一个线程用 volatile 修改变量时:
- JVM 会为该写操作插入一条带 lock 前缀的汇编指令(如
lock xadd),强制 CPU 执行“缓存锁定”而非“总线锁定” - 当前 CPU 核心将对应缓存行状态从 E(独享) 或 S(共享) 变为 M(已修改),并标记为“脏”
- 在后续将该值写回主内存前,通过总线嗅探(Bus Snooping)机制广播通知:其他核心若缓存了同一地址的副本,必须将其状态设为 I(无效)
- 其他线程下次读取该 volatile 变量时,发现本地缓存行是 I 状态,就会自动触发 remote read——从主内存重新加载最新值到自己的缓存中
为什么普通变量做不到,而 volatile 可以?
关键在于是否触发 MESI 的状态变更与嗅探广播:
- 普通变量的写操作只是更新本地缓存,状态可能长期停留在 S 或 E,不触发广播,其他核心缓存仍为 S,继续读旧值
- volatile 写操作强制进入 M 状态,并伴随 lock 指令,激活嗅探机制,使其他核心的对应缓存行变为 I,从而打破“静默缓存”
- volatile 读操作本身也会插入内存屏障(Load Barrier),禁止重排序,并确保读取前检查缓存行有效性——若为 I,就强制 reload
注意:MESI 不解决原子性,只管“谁能看到新值”
MESI 协议保障的是数据在多核间的一致性传播路径,但它不干预指令执行逻辑:
- 像
count++这种“读-改-写”复合操作,即使 count 是 volatile,两个线程仍可能同时读到旧值 100,各自加 1 后都写回 101——结果丢失一次更新 - 这是因为 MESI 只保证“写完后别人能看见”,但不保证“读和写之间不被插队”。原子性需要 synchronized、CAS 或 AtomicXXX 类来实现

















