锁消除是删掉不可能竞争的锁,锁粗化是将循环内多次加锁合并为一次;二者均由JIT(C2)在热点代码阶段基于逃逸分析和运行时行为分析自动优化,不改变逻辑但显著降低同步开销。

锁消除和锁粗化是 JVM 在运行时自动完成的两种锁优化技术,它们不改变代码逻辑,却能显著降低同步开销。关键在于:锁消除是“删掉没用的锁”,锁粗化是“把零碎锁合并成一个大锁”。两者都依赖 JIT 编译器(特别是 C2)在热点代码阶段生效,不是写完代码立刻起效。
锁消除:去掉不可能竞争的同步
它的基础是逃逸分析——JVM 判断一个对象是否可能被其他线程访问。如果确定不会逃逸出当前线程(比如方法内的局部对象),那它上面的 synchronized 就是多余的。
- 典型例子是 StringBuffer 的局部使用:每个线程调用自己的 StringBuffer 实例,所有 append 操作本就不需要线程安全,JIT 会直接移除内部的 synchronized 调用,性能接近 StringBuilder。
- 局部 new 出来的锁对象(如
Object lock = new Object())且只在当前方法内用于 synchronized 块,也常被消除。 - 注意:必须开启逃逸分析(JDK6+ 默认开启),且对象不能被返回、赋值给静态字段、作为参数传给未知方法等,否则逃逸成立,无法消除。
锁粗化:合并重复加锁的连续操作
当同一段代码中反复对同一个锁对象加锁/解锁,而中间又没有其他线程必须感知的数据变更时,JVM 会把锁范围扩大,减少锁操作次数。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 最常见的是 循环体内的 synchronized:比如 for 循环里每次迭代都
synchronized(this) { doWork(); },会被优化为整个循环外加一次锁。 - 还有连续多个独立的 synchronized 块作用于同一对象,例如先后对某个 list 执行 add、get、clear,也可能被合并成一个大块。
- 前提很关键:锁对象必须相同、操作必须由同一线程执行、中间不能有影响可见性的共享状态更新(比如修改了另一个线程正在读的 volatile 变量)。
怎么让这些优化真正起作用
它们不会在刚启动或简单测试中出现,因为 JIT 需要先收集运行时信息,等方法成为热点后才触发 C2 编译。
立即学习“Java免费学习笔记(深入)”;
- 确保使用 server 模式(JDK8+ 通常默认,可检查
java -version输出是否含 “Server VM”)。 - 避免在 main 方法或单元测试里短时间跑几轮就下结论;应做持续压测(如 JMH),让方法执行数千次以上。
- 不要人为“提前加锁”或“拆分锁块”来迎合优化——JVM 自己会判断;写代码时仍应遵循最小锁粒度原则,保持逻辑清晰。
- 可通过
-XX:+PrintCompilation和-XX:+UnlockDiagnosticVMOptions -XX:+PrintEscapeAnalysis观察编译日志和逃逸分析结果。
这些优化不是开发者手动控制的开关,而是 JVM 对“安全但低效”的代码模式做出的智能响应。理解它们,是为了写出更符合 JIT 直觉的代码,而不是绕着它们设计逻辑。


















