锁消除与锁粗化是JVM JIT编译器基于逃逸分析和同步模式识别实施的运行时优化:锁消除移除无竞争的局部对象同步,锁粗化合并连续同对象的小粒度锁,二者均不改变程序语义与happens-before关系。

锁消除:删掉根本不需要的 synchronized
锁消除不是跳过内存屏障,而是证明某段同步代码在逻辑上永远不会面临竞争,因此连锁操作本身都可以省略。
- 核心依据是逃逸分析:JVM 判断一个对象是否只被当前线程访问、未发布到堆外(如没赋值给 static 字段、没传入未知方法、没放入共享容器),即“未逃逸”
- 典型例子是
new StringBuffer().append(...):StringBuffer 是同步类,但局部创建的对象不会被其他线程看到,JIT 就会把每个append()内部的synchronized完全移除 - 效果等同于改用 StringBuilder,但无需修改源码——只要对象不逃逸,JMM 的可见性保证本来就不需要,删锁不破坏任何 happens-before 链
锁粗化:把多个小锁合并成一个大锁
锁粗化不削弱同步强度,反而扩大临界区范围,但它避免了重复进入 Monitor 带来的系统级开销(比如用户态/内核态切换、队列管理、上下文保存)。
- 触发条件是连续、同对象、无中间共享写操作的 synchronized 块,例如循环体内反复加同一把锁
- 优化前:
for (int i = 0; i - 优化后:
synchronized(obj) { for (int i = 0; i ——加锁/解锁从 100 次降到 1 次 - JMM 保证依然成立:整个循环体仍受同一锁保护,所有对共享变量的读写仍遵循 synchronized 的 happens-before 规则
它们依赖的关键运行时支持
这两项优化不是写代码时能手动控制的,而是由 JVM 在热点代码编译阶段自动启用:
- 必须开启逃逸分析(
-XX:+DoEscapeAnalysis,JDK8+ 默认开启) - 锁消除需显式启用(
-XX:+EliminateLocks,JDK8 起默认开启) - 仅在 C2 编译器(server 模式)对高频执行的方法生效,冷启动或调试模式下看不到
- 验证方式:用
-XX:+PrintCompilation和-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis)观察编译后指令变化
注意边界:优化不是万能的
锁消除和锁粗化都有严格前提,违反就可能失效甚至被禁用:
立即学习“Java免费学习笔记(深入)”;
- 对象一旦发生逃逸(比如被存入
static Map或传给Executor.submit()),逃逸分析失败,锁消除立即失效 - 锁粗化要求中间不能有影响可见性的操作(如对 volatile 变量赋值、调用 notify()),否则 JIT 不敢合并
- 如果锁对象本身是全局单例或静态字段,粗化可能扩大竞争范围,反而降低并发度——JVM 会权衡,不一定粗化



















