锁粗化是jit编译器自动将同一把锁的多个连续无竞争临界区合并为一个以减少开销的优化,需满足同锁、紧邻、无锁释放点、语义不变及低竞争等条件。

Java 锁粗化(Lock Coarsening)是 JIT 编译器在运行时自动执行的一种优化,它不会由程序员手动“把多次加锁合并为一次”,而是由 HotSpot JVM 在满足特定条件时,将**同一把锁的多个连续、无间断、无竞争的临界区**合并成一个更大的临界区,从而减少加锁/解锁的开销。
锁粗化的触发条件
JIT 编译器(如 C2)会在以下情况考虑锁粗化:
- 多个
synchronized块使用的是**同一个对象锁**(或同一把可重入锁) - 这些同步块在字节码层面是**紧邻、连续、中间没有其他同步操作或可能引发锁释放的操作**(如 wait()、I/O、线程阻塞、synchronized 其他锁等)
- 编译器能静态或动态判定:合并后**不会改变程序语义**(即没有其他线程能在此期间获取该锁)
- 运行时 profiling 显示该代码段高频执行,且锁竞争很低
典型可被粗化的代码模式
下面这段代码常被 JIT 识别并优化:
synchronized (obj) {
a++;
}
synchronized (obj) {
b++;
}
synchronized (obj) {
c++;
}
若 JIT 判定这三段之间无锁释放点、无外部可见副作用,且 obj 不变,就可能将其粗化为:
synchronized (obj) {
a++;
b++;
c++;
}
注意:这不是源码改写,而是 JIT 在生成本地代码时对同步边界做的重排。
不能依赖手动“模拟粗化”的原因
你无法通过简单地把多个 synchronized 块“手动合并”来稳定获得性能提升,因为:
- 合并后临界区变长 → 持锁时间增加 → 其他线程等待概率上升 → 可能反而降低并发度
- 若中间有非原子操作(如日志、IO、调用外部方法),手动合并会扩大锁范围,引入不必要的串行化
- JIT 的粗化是基于运行时反馈的智能决策;手动改写可能破坏其优化前提,甚至让 JIT 放弃优化
真正有效的实践建议
与其关注“怎么合并”,不如聚焦可控的优化方向:
- 优先减少锁粒度:用更小的锁对象(如分段锁、ConcurrentHashMap)、读写锁(ReentrantReadWriteLock)或无锁结构(CAS、AtomicXXX)
- 避免在同步块内做耗时操作:如网络调用、磁盘 IO、复杂计算 —— 这些会让锁持有时间不可控,既阻碍 JIT 粗化,也损害并发
- 确认是否真需要锁:局部变量、线程封闭对象(ThreadLocal)、不可变对象无需同步
- 用 JMH 做微基准测试:对比不同同步范围的实际吞吐与延迟,而非凭直觉调整
锁粗化是 JVM 的“隐形助手”,不是编程接口。理解它的存在,是为了不干扰它,而不是去模拟它。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











