JMM本身不直接实现可见性,而是通过规范volatile等语义,由JVM插入内存屏障指令(如StoreLoad/LoadLoad),协同CPU的MESI协议触发缓存行失效与重载,最终落实多核间数据同步。

Java 内存模型(JMM)本身不直接实现可见性,而是通过规范 volatile、synchronized 等语义,把对共享变量的读写操作“翻译”成能触发底层硬件行为的指令,最终由 CPU 的缓存一致性协议(如 MESI)来落实数据同步。
可见性问题的根源在多核缓存副本
每个 CPU 核心都有独立的 L1/L2 缓存,线程运行时操作的是本地缓存行(cache line),不是主内存。比如线程 A 在 Core0 把变量 x 改为 1,只更新了自己缓存;线程 B 在 Core1 仍读着旧值 0 的副本——这不是 Java 的缺陷,而是现代 CPU 为性能做的合理设计。
真正要解决的,是在不牺牲速度的前提下,让各核缓存“步调一致”。JMM 不处理这个,它把任务交给了硬件。
JVM 将 volatile 映射为内存屏障 + MESI 协议信号
当你声明 volatile int x,JVM 并不会魔法般同步所有缓存。它会在编译和运行时做两件事:
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 对每次 volatile 写(如 x = 1),插入 StoreStore + StoreLoad 内存屏障:前者确保前面的普通写已刷出,后者强制将新值写回主内存,并广播缓存行失效请求(RFO)
- 对每次 volatile 读(如 int v = x),插入 LoadLoad + LoadStore 内存屏障:前者禁止后续读被重排到该读之前,后者保证该读一定在后续写之前完成,并触发缓存状态检查
这些屏障最终会转为 CPU 指令(如 x86 的 mfence 或带 lock 前缀的操作),直接驱动硬件行为。
MESI 协议执行实际的数据同步
MESI 是 Intel/AMD 等主流 CPU 内置的状态机协议,管理每个缓存行的生命周期:
- Modified(M):本核修改过,数据已脏,仅本核持有最新值
- Exclusive(E):本核独占,内容干净,可直接写入升级为 M
- Shared(S):多个核都缓存了干净副本;任一核要写,必须先发失效请求,让其他 S 态变 Invalid
- Invalid(I):该缓存行无效;下次访问必须重新加载(从主存或 M/E 态缓存取)
volatile 写触发 RFO 后,其他核把对应缓存行设为 I;volatile 读遇到 I 态,就强制重载——这就完成了“改了就能被看到”的闭环。
普通变量为什么不行
普通变量读写没有内存屏障约束,JVM 可能优化掉冗余读、合并写、重排指令;CPU 缓存也按默认策略运行,不会主动失效或重载。结果就是:线程 A 写完可能卡在写缓冲区没刷出,线程 B 一直读自己缓存里的旧值,陷入死循环。
volatile 不是“自动同步”,它是 JVM 和 CPU 协同的一条明确通路:Java 层提要求 → JVM 插屏障 → CPU 执行 MESI 流程 → 数据真正可见。

















