锁粗化与锁消除是JVM JIT编译器自动执行的两种锁优化:锁粗化合并连续小粒度锁以减少开销,锁消除基于逃逸分析移除无竞争的锁,二者均不改变语义、无需手动干预,仅在热点代码C2编译后生效。

Java 锁机制中,锁粗化和锁消除是 JVM 在 JIT 编译阶段自动完成的两种关键优化手段,不依赖程序员显式干预,但理解其原理能帮你写出更易被优化的代码。它们不改变程序语义,只减少无意义的同步开销,从而直接提升字节码执行效率。
锁粗化:合并连续的小锁,降低上下文切换成本
当同一个线程在一段代码中反复对同一对象加锁、解锁(比如循环体或相邻同步块),JVM 会识别这种“高频低竞争”模式,并把多个细粒度锁合并成一个更大范围的锁。这样避免了重复的 monitorenter/monitorexit 指令调用,减少了锁状态维护、内存屏障插入及潜在的线程调度开销。
- 典型场景:循环内多次 synchronized 调用同一锁对象,或方法内连续几个小同步块操作同一临界资源
- 触发前提:必须是同一线程、同一锁对象、中间无跨线程可见性要求的操作(如 volatile 写、notify 等)
- 效果:原本 5 次加锁解锁可能被压缩为 1 次,执行路径更紧凑,CPU 流水线更友好
锁消除:删掉根本不会竞争的锁,归零同步开销
锁消除依赖逃逸分析(Escape Analysis),JVM 判断某个锁对象是否“逃逸”出当前线程的作用域。如果确认该对象仅被当前线程使用(例如方法内新建的 StringBuffer、局部创建的锁对象),那么它的 synchronized 就是冗余的——没有其他线程能访问它,自然无需互斥。
- 常见目标:StringBuffer.append()、局部 new Object() 后的 synchronized 块、不可变参数封装的同步逻辑
- 生效条件:需开启 Server 模式(默认已启用)、逃逸分析未被禁用(-XX:+DoEscapeAnalysis)、对象确实未逃逸
- 效果:完全移除 monitorenter/monitorexit 字节码,同步块退化为普通顺序执行,性能接近非线程安全版本(如 StringBuilder)
怎么让代码更容易被这两种优化命中
你不需要手动“加粗化”或“写消除”,但可以主动规避阻碍优化的写法:
立即学习“Java免费学习笔记(深入)”;
- 避免在循环内创建新锁对象(如 new Object() 放在 for 里),这会让逃逸分析失效,也破坏锁粗化前提
- 尽量使用局部变量封装共享操作,而非将锁对象作为类字段长期持有并频繁进出同步块
- 不要在同步块中调用可能引起对象逃逸的方法(如把局部对象传给外部集合、赋值给 static 字段等)
- 确认运行时启用了相关优化:现代 JDK(8u292+ / 11+)默认开启逃逸分析与锁优化,可通过 -XX:+PrintCompilation 观察编译日志中的优化痕迹
它们不是万能的,但值得信任
锁粗化和锁消除由 HotSpot JIT 自动决策,发生在运行时热点代码编译阶段。它们不解决高竞争场景,也不替代合理设计(如分段锁、CAS)。但对大量存在“伪共享”或“单线程高频同步”的业务逻辑(如 JSON 序列化、配置组装、本地缓存构建),这类优化能带来可观的吞吐提升——往往比手动调优更稳定、更安全。


















