mesi协议是volatile可见性的硬件基础:写volatile时触发rfo使其他核缓存行置为invalid,读时因状态为invalid而强制重加载最新值,并结合内存屏障确保缓冲区刷新与指令有序。

MESI协议是Java内存模型(JMM)中可见性问题的硬件级支撑机制,它不直接由Java定义,而是现代多核CPU为解决缓存不一致而内置的底层规则。Java的volatile、synchronized等语义,最终都依赖MESI在硬件层面落地执行。
MESI的四种状态如何反映数据归属与新鲜度
每个CPU缓存行(cache line)被标记为以下一种状态,决定它能否被读写、是否需要同步:
- Modified(M):该缓存行数据已被当前CPU修改,与主内存不一致;且仅本CPU持有该最新副本。下次其他CPU要读它时,必须先让本CPU把M态数据写回内存(或直传),再更新对方缓存。
- Exclusive(E):本CPU独占该缓存行,内容与主内存完全一致;未被修改过。此时可直接写入(升级为M态),无需广播通知其他CPU。
- Shared(S):多个CPU缓存中都存有该数据的干净副本,且都与主内存一致。一旦任一CPU要写,必须先向所有S态缓存发送“失效请求”,将它们全部置为Invalid,自己才能转为M态写入。
- Invalid(I):该缓存行无效,不能用于读写。CPU若需访问对应变量,必须从主内存或某个M/E态缓存重新加载,状态随之变为S或E。
一次volatile写操作触发的MESI状态流转
假设线程A在Core0上对volatile变量x执行写操作(x = 1),而线程B在Core1中已缓存了x的旧值:
- Core0发现x所在缓存行处于E或S态,准备写入;若为S态,先广播“invalidate”消息,要求其他核心将x所在缓存行设为I态。
- Core1收到消息后,立即将本地x缓存行标记为I;后续Core1若读x,会触发cache miss,必须重新加载。
- Core0完成写入,缓存行进入M态;当Core1再次读x时,要么从Core0的缓存直取(cache-to-cache fill),要么从主内存加载——总之拿到的是Core0刚写的新值。
为什么volatile能保证可见性,而普通变量不能
关键区别在于JVM对volatile读写的编译处理:
- 对volatile变量的每次写,JVM会插入
StoreStore + StoreLoad内存屏障,强制刷新写缓冲区,并确保MESI的失效广播及时发出。 - 对volatile变量的每次读,JVM插入
LoadLoad + LoadStore屏障,禁止指令重排,并强制检查缓存行状态——若为I态,必须重新加载,不会复用旧值。 - 普通变量读写无屏障,编译器和CPU可能优化掉冗余读、合并多次写;即使底层发生MESI状态变化,JVM也不强制感知或等待,导致线程看到过期副本。
MESI不是万能的:它只管缓存,不管JVM重排序与指令优化
MESI解决了“物理数据同步”,但Java程序还面临两层抽象干扰:
- 编译器可能把非volatile变量的多次读合并为一次,或把写延迟到方法末尾——这些优化绕过了缓存一致性机制。
- CPU写缓冲区(store buffer)会让M态更新延迟对外可见;读缓冲区(invalidation queue)也可能延迟处理失效消息。内存屏障的作用,正是约束这些缓冲区的行为,让MESI协议真正生效。
- 所以单靠MESI不能直接保证Java可见性;必须配合JMM定义的内存屏障语义,软硬协同,才构成完整的可见性保障链条。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











