synchronized通过触发mesi协议的缓存同步动作保障可见性:进入时使其他核缓存行失效,退出时强制刷新所有修改变量回主内存并广播invalidate消息,非仅靠锁机制本身。

synchronized 在多核 CPU 下不是靠“加锁”本身保证可见性,而是通过触发底层缓存同步动作,与 MESI 协议协同工作,强制让其他核心看到最新值。
退出 synchronized 会刷新所有变量,不只是 volatile
Java 语言规范(JLS 17.4)明确要求:线程退出 synchronized 块时,必须将本线程本地修改的全部变量(包括普通 int、对象字段、数组元素等)写回主内存。这不是 JVM 的优化选择,而是语义强制约束。
这个“写回”过程在硬件层表现为:
- 清空当前 CPU 的 Store Buffer(避免写操作滞留在缓冲区)
- 等待本核 Invalidating Queue 中的失效请求被处理完毕
- 向总线广播 Invalidate 消息,使其他核对应缓存行状态变为 Invalid(I)
- 其他核下次读该地址时,必须从主内存或拥有 M/E 状态的核重新加载——从而拿到新值
MESI 状态变化是 synchronized 可见性的物理基础
假设变量 x 初始在 Core0 和 Core1 的缓存中都为 Shared(S)状态:
- Core0 进入 synchronized 块并修改 x → 先广播 Read-Exclusive 请求 → Core1 将其缓存行置为 Invalid(I)→ Core0 获得 Exclusive(E),再转为 Modified(M)
- Core0 退出 synchronized 块 → 强制将 x 写回主内存(M→E→S)→ 同时广播 Invalidate → Core1 缓存行彻底失效
- Core1 后续进入 synchronized 块读 x → 发现缓存失效 → 触发 remote read → 从主内存或 Core0 加载最新值
整个流程不依赖软件轮询,全由硬件基于 MESI 状态机自动完成。
性能开销来自缓存行在核间反复迁移
synchronized 的代价主要不在“锁竞争”,而在 MESI 协议引发的缓存一致性流量:
- 高争用下,同一缓存行在多个核之间频繁切换状态(S↔I↔M),称为 cache line bouncing
- 每次状态转换都需总线广播和响应,延迟远高于本地缓存访问(纳秒级 vs 微秒级)
- 相比 volatile(只刷 Store Buffer + 加轻量屏障),synchronized 的屏障序列更重,跨核同步更彻底
所以锁粒度要合理:太粗导致串行化严重;太细则让 MESI 开销成为瓶颈(比如为数组每个元素配一把锁)。
偏向锁/轻量级锁能绕过 MESI 同步
在无竞争场景下,HotSpot 的偏向锁和轻量级锁通过 CAS 和栈帧标记实现同步,完全不触发总线广播或缓存行失效。
只有当发生真实竞争、锁膨胀为重量级锁时,才转入操作系统互斥量(Mutex),此时才真正调用底层同步原语,激活 MESI 协议参与跨核数据同步。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











