锁粗化将循环内多次加锁解锁合并为一次,大幅减少monitorenter/monitorexit指令执行、cas操作、内存屏障及线程状态切换等底层开销,从而释放被同步机制占用的cpu周期。
锁粗化是jvm在编译期或运行期对连续的、无竞争的synchronized代码块进行合并优化的技术,目的是减少频繁加锁/解锁带来的开销。
为什么需要锁粗化
当一段逻辑中存在多个相邻的、锁同一对象的synchronized块时,比如循环体内反复加锁:
for (int i = 0; i <p>若不优化,就会执行100次monitorenter + monitorexit指令,每次都要检查锁状态、更新Mark Word、可能触发线程挂起/唤醒——即使全程只有当前线程访问。这种高频轻量操作反而比一次长临界区更耗资源。</p> <h3>锁粗化的触发条件</h3> <p>JVM(特别是HotSpot)会在以下情况自动启用锁粗化:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6235" title="Java Maven Code Review"><img src="https://img.php.cn/upload/skill/000/000/081/179084711841712.jpg" alt="Java Maven Code Review" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill6235" title="Java Maven Code Review" class="overflowclass">Java Maven Code Review</a> <p class="overflowclass">审查Java Maven项目(ZIP压缩包或GitLab仓库URL),检查代码规范、命名、模块边界、可维护性问题以及重复代码。</p> </div> <a rel="nofollow" href="/xiazai/skill6235" title="Java Maven Code Review" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
- 多个synchronized块作用于**同一个锁对象**,且中间**没有其他同步块或锁释放行为**;
- 这些块在**字节码层面连续或逻辑上可判定为串行无间断**(如简单for循环体、相邻语句块);
- JIT编译器(C2)在方法内联和控制流分析后,确认这些块之间**不存在线程安全风险**(例如无共享变量写入、无wait/notify调用、无跨线程可见副作用)。
底层怎么实现粗化
不是真的“合并字节码”,而是在JIT编译阶段重写同步边界:
- 将原本分散的monitorenter/monitorexit指令对,替换为**外层一个大的monitorenter + 一个monitorexit**;
- 对象头中的锁状态(Mark Word)只在粗化后的入口处升级/获取,在出口处统一释放;
- 若原代码块中已存在轻量级锁或偏向锁,粗化后仍沿用对应锁级别,避免提前升级为重量级锁;
- 粗化后的临界区变长,但只要无真实竞争,就始终停留在轻量级锁甚至偏向锁状态,避免进入操作系统Mutex等待队列。
锁粗化与锁消除、自旋的区别
三者都是JVM锁优化策略,但目标不同:
- 锁消除:发现某段同步代码实际**不会被多线程访问**(如局部对象、逃逸分析确定私有),直接删掉monitorenter/monitorexit;
- 锁粗化:保留同步语义,但把**多次小同步合并成一次大同步**,减少指令数和状态切换;
- 自适应自旋:发生在锁竞争时,线程不立即挂起,而是**在用户态循环检查锁是否释放**,适用于锁持有时间极短的场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










