锁粗化是JVM运行时由C2编译器自动触发的优化,将同一对象、同一线程下连续且无干扰的多次加锁解锁合并为一次大范围加锁,减少同步开销。

锁粗化是 JVM 在运行时自动做的优化,核心思路是把同一段代码里反复出现的、针对同一个对象的多次加锁解锁操作,合并成一次更大范围的加锁。它不改变程序逻辑,但能显著减少锁的申请和释放次数,从而降低上下文切换、Monitor 竞争等开销。
锁粗化生效的关键条件
JVM 不会随意合并锁,只有满足以下几点,C2 编译器才可能触发锁粗化:
-
同一对象、同一线程连续操作:所有 synchronized 块锁的是同一个对象(比如都是
synchronized(lock)),且由当前线程依次执行,中间没有被其他线程打断或插入异步逻辑。 -
中间无共享状态变更需求:被合并的多个同步块之间,不能有需要对其他线程立即可见的数据修改。例如,连续读取集合内容可以粗化;但如果中间穿插了
list.add()又马上要被另一线程感知,就大概率不会粗化。 -
没有跨方法边界逃逸干扰:如果锁对象被传入未知外部方法(如
logger.info(obj))、存入 static 字段或 ThreadLocal 之外的容器,JVM 的逃逸分析可能受限,间接影响锁粗化的判断依据。
典型可粗化的代码模式
这些写法容易被 JIT 编译器识别并优化:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
循环体内重复加锁:
原始写法中每次迭代都加锁解锁,开销叠加;JVM 可能将其提升为整个循环外加一次锁。 -
相邻的多个小同步块:
比如连续三行分别对同一 lock 对象做 set、get、clear 操作,会被合并为一个大的 synchronized 块。 -
调用链中多个 synchronized 方法操作同一实例:
例如obj.doA()、obj.doB()、obj.doC()都是 synchronized 实例方法,且 obj 是局部变量、未逃逸,也可能被粗化为一次锁定 obj 执行全部调用。
实际效果与注意事项
锁粗化后,字节码层面的 monitorenter/monitorexit 指令数量减少,不仅省去锁操作本身,还让同步块更易被内联、进一步触发其他 JIT 优化。
立即学习“Java免费学习笔记(深入)”;
- 不是手动控制的机制:开发者无需显式开启或配置,它是 JIT 编译器在峰值性能阶段(C2 编译后)自动完成的,解释执行或 C1 编译阶段通常看不到效果。
- 别为了粗化而刻意写碎锁:虽然 JVM 能合并,但代码可读性和维护性更重要。优先保证逻辑清晰,再依赖 JVM 做底层优化。
-
验证需看汇编或 JFR 日志:本地简单测试常因预热不足、编译阈值未达等原因看不到粗化效果,建议用 JMH + JVM 参数(如
-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly)辅助确认。


















