锁粗化将循环内多次加锁解锁合并为一次,大幅减少monitorenter/monitorexit指令执行、CAS操作、内存屏障及线程状态切换等底层开销,从而释放被同步机制占用的CPU周期。
锁粗化是jvm在编译期或运行期对连续的、无竞争的synchronized代码块进行合并优化的技术,目的是减少频繁加锁/解锁带来的开销。
为什么需要锁粗化
当一段逻辑中存在多个相邻的、锁同一对象的synchronized块时,比如循环体内反复加锁:
for (int i = 0; i < 100; i++) {
synchronized(lock) {
doSomething(i);
}
}
若不优化,就会执行100次monitorenter + monitorexit指令,每次都要检查锁状态、更新Mark Word、可能触发线程挂起/唤醒——即使全程只有当前线程访问。这种高频轻量操作反而比一次长临界区更耗资源。
锁粗化的触发条件
JVM(特别是HotSpot)会在以下情况自动启用锁粗化:
- 多个synchronized块作用于**同一个锁对象**,且中间**没有其他同步块或锁释放行为**;
- 这些块在**字节码层面连续或逻辑上可判定为串行无间断**(如简单for循环体、相邻语句块);
- JIT编译器(C2)在方法内联和控制流分析后,确认这些块之间**不存在线程安全风险**(例如无共享变量写入、无wait/notify调用、无跨线程可见副作用)。
底层怎么实现粗化
不是真的“合并字节码”,而是在JIT编译阶段重写同步边界:
立即学习“Java免费学习笔记(深入)”;
- 将原本分散的monitorenter/monitorexit指令对,替换为**外层一个大的monitorenter + 一个monitorexit**;
- 对象头中的锁状态(Mark Word)只在粗化后的入口处升级/获取,在出口处统一释放;
- 若原代码块中已存在轻量级锁或偏向锁,粗化后仍沿用对应锁级别,避免提前升级为重量级锁;
- 粗化后的临界区变长,但只要无真实竞争,就始终停留在轻量级锁甚至偏向锁状态,避免进入操作系统Mutex等待队列。
锁粗化与锁消除、自旋的区别
三者都是JVM锁优化策略,但目标不同:
- 锁消除:发现某段同步代码实际**不会被多线程访问**(如局部对象、逃逸分析确定私有),直接删掉monitorenter/monitorexit;
- 锁粗化:保留同步语义,但把**多次小同步合并成一次大同步**,减少指令数和状态切换;
- 自适应自旋:发生在锁竞争时,线程不立即挂起,而是**在用户态循环检查锁是否释放**,适用于锁持有时间极短的场景。


















