volatile写通过mesi协议强制使其他核缓存行置为invalid,读则强制从最新源加载;内存屏障与mesi协同建立happens-before关系;伪共享会因缓存行失效放大性能损耗。

Java 中 volatile 变量的读写操作,会直接触发底层 CPU 的缓存一致性协议(主要是 MESI),不是“通知”或“等待”,而是通过硬件指令强制改变缓存行状态,从而让多核之间对共享变量的视图保持同步。
volatile 写操作:让其他核缓存行变无效
当一个线程写 volatile 变量时,JVM 会生成带 lock 前缀 的汇编指令(如 lock xchg)。该指令在 x86 上不锁总线,而是启动“缓存锁定”:
- CPU 将该变量所在缓存行(64 字节)状态从 S(Shared)或 E(Exclusive) 升级为 M(Modified);
- 同时广播 RFO(Read For Ownership)请求,其他核心通过总线嗅探(Bus Snooping)监听到该请求;
- 若其他核缓存中存在同一地址的缓存行,且处于 S 或 M/E 状态,就立即将其置为 I(Invalid);
- 发起写操作的核心完成修改后,会将新值写回 L3 缓存或主内存,并确保其他核下次读取时无法命中本地缓存。
volatile 读操作:强制重新加载最新副本
读 volatile 变量时,JVM 插入 LoadLoad + LoadStore 内存屏障,并促使 CPU 检查缓存行状态:
- 如果该缓存行当前是 I(Invalid) 状态(比如刚被其他核写过),CPU 不会尝试使用旧值,而是立即触发 cache miss;
- 接着通过缓存一致性协议,从主内存、L3 缓存,或拥有最新值的其他核缓存中加载数据;
- 即使没有被其他线程改过,只要缓存行曾被标记为 I,这次读也一定拿到最新副本——不是“轮询”,而是硬件级强制刷新本地视图。
内存屏障与 MESI 协同建立 happens-before
volatile 的有序性不是靠软件排队,而是由内存屏障约束指令顺序,并由 MESI 状态流转落地执行:
- volatile 写后插入 StoreLoad 屏障,确保写操作完成、缓存行状态同步广播完毕后,后续读写才可执行;
- volatile 读前插入 LoadLoad 屏障,保证前面所有读写已提交,再开始本次读;
- MESI 中的 M → I → S 状态变化天然构成同步点,使“写 volatile”happens-before“后续任意线程读该 volatile”。
伪共享会让这个机制暴露性能代价
volatile 本身不解决伪共享,但它的高频写行为会放大问题:
- 两个 volatile 字段若落在同一缓存行(如相邻 long),一个核写第一个字段,会令整行失效;
- 另一个核哪怕只读第二个字段,也会因缓存行 I 状态而被迫重新加载,产生无谓总线流量;
- 这时需用字节填充(padding)或
@Contended注解,让关键 volatile 字段独占缓存行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











