synchronized进入临界区时使本地内存中相关共享变量副本失效,退出时将修改过的共享变量刷新回主内存,这是JMM强制保证的可见性语义,非可选优化。

当线程进入或退出 synchronized 临界区时,Java 内存模型(JMM)会强制执行一套变量同步动作:释放锁前把本地内存中被修改的共享变量刷新回主内存;获取锁时则让本地内存中对应共享变量的副本失效。这不是可选优化,而是 JMM 规定的语义保障。
synchronized 进入临界区:本地内存失效
线程在成功获得锁(即进入 synchronized 块/方法)的瞬间,JMM 要求该线程将自己工作内存中所有与被锁对象相关联的共享变量副本标记为“无效”。后续对该变量的任何读取,都必须从主内存重新加载最新值。
- 失效范围仅限于被监视器(monitor)保护的共享变量,不包括局部变量或未被其他线程并发访问的字段
- 这个动作由 JVM 在底层插入内存屏障(Memory Barrier)实现,确保不会被编译器或 CPU 重排序绕过
- 即使变量之前未被修改,只要它属于临界区可能访问的共享状态,其本地副本仍会被清空
synchronized 退出临界区:共享变量刷新回主内存
线程在释放锁(临界区结束)前,必须将其本地内存中所有被修改过的共享变量,全部写回主内存。这一刷新动作对其他等待该锁的线程可见,构成线程间通信的基础路径。
- 刷新是“全量触发”而非“按需写入”——只要变量在临界区内被写过,就一定回写,不管是否显式声明为 volatile
- 刷新顺序不保证与代码中赋值顺序完全一致,但受 happens-before 规则约束:解锁操作 happens-before 后续同锁的加锁操作
- 若多个线程反复争用同一把锁,这种“失效→读主存→修改→刷新”循环就构成了可见性传递链
为什么 volatile 不能替代 synchronized 的刷新/失效机制?
volatile 只保证单个变量的读写可见性和禁止重排,但它不提供原子性,也不建立临界区边界。synchronized 的刷新与失效是成对、有边界的同步契约:
- volatile 写后立即刷主存,但读时不强制刷新整个本地内存;synchronized 获取锁时会批量使多个共享变量副本失效
- volatile 无法阻止两个线程同时进入逻辑上应互斥的操作段;synchronized 通过锁的排他性天然隔离了临界区执行流
- 多个 volatile 变量组合读写仍可能存在中间态(如先读 a 再读 b),而 synchronized 包裹的整段代码被视为一个不可分割的观察窗口
实际影响:别依赖“没加锁却刚好看到新值”
没有 synchronized(或等效同步手段)保护的共享变量读写,JMM 不承诺任何刷新或失效行为。即使某次运行中你“碰巧”看到了另一个线程写入的值,也只是缓存巧合或 JVM 实现细节,并非规范保证。
- 不要用“变量似乎更新了”来验证线程安全;必须依据 happens-before 和锁语义设计逻辑
- 局部变量、final 字段、ThreadLocal 等不受此机制影响,它们本就不参与跨线程共享
- 锁对象本身(如 new Object())是否被其他地方引用,不影响其作为同步点的功能,但会影响锁的粒度和竞争范围

















