锁升级机制全解析:偏向锁、轻量级锁、重量级锁的秘密

摘要

在 HotSpot JVM 中,synchronized 并不是一开始就进入重量级锁。为了提升性能,JVM 设计了 锁升级机制:偏向锁 → 轻量级锁 → 重量级锁。本文将带你深入理解三种锁的特点、实现原理与适用场景,彻底搞清楚锁升级的秘密。


正文

一、为什么需要锁升级?

并发场景下,锁竞争频率差异巨大

  • 有时几乎无竞争;
  • 有时存在少量竞争;
  • 还有时竞争激烈。

如果一上来就使用重量级锁(基于操作系统的互斥量),会导致频繁 用户态 ↔ 内核态切换 ,性能开销大。

因此 JVM 引入了 分级锁机制,根据竞争情况逐步升级。


二、锁的三种形态

1. 偏向锁(Biased Lock)
  • 设计初衷 :优化 无竞争场景
  • 特点:锁会"偏向"第一个获取它的线程,如果后续没有其他线程竞争,该线程再次进入时无需加锁。
  • 实现方式 :在对象头的 Mark Word 中记录线程 ID,下次判断时只需比对线程 ID。
  • 适用场景:大量单线程操作的场景,比如对象被一个线程反复使用。
java 复制代码
synchronized(lock) {
    // 偏向锁:几乎零开销
}
2. 轻量级锁(Lightweight Lock)
  • 设计初衷 :优化 少量竞争场景
  • 特点 :多个线程尝试获取锁时,使用 CAS(Compare-And-Swap) 操作在用户态竞争,不涉及内核。
  • 实现方式 :JVM 会在当前线程栈中创建一个 锁记录(Lock Record) ,并尝试用 CAS 将对象头的 Mark Word 指向它。
  • 适用场景:锁竞争不激烈,但可能有多个线程交替进入的情况。
3. 重量级锁(Heavyweight Lock)
  • 设计初衷 :解决 激烈竞争场景
  • 特点:多个线程竞争失败后进入阻塞状态,由操作系统调度,唤醒时需要内核态切换,开销大。
  • 实现方式:Mark Word 指向 Monitor 对象,失败线程进入 Monitor 的 EntryList 队列等待。
  • 适用场景:高并发、竞争激烈,无法避免阻塞。

三、锁的升级过程

锁状态在 JVM 中的演进顺序如下:

无锁 → 偏向锁 → 轻量级锁 → 重量级锁

  • 偏向锁 → 轻量级锁:当另一个线程加入竞争时,偏向锁会撤销并升级为轻量级锁。
  • 轻量级锁 → 重量级锁:当 CAS 自旋失败或竞争严重时,锁升级为重量级锁,线程进入阻塞。

⚠️ 注意:锁只能升级,不能降级(除非对象被回收再分配)。


四、直观对比

锁类型 开销大小 是否阻塞 适用场景
偏向锁 最小 单线程反复使用对象
轻量级锁 中等 否(自旋) 少量竞争
重量级锁 最大 高并发激烈竞争

五、工程实践中的建议

  1. 减少锁的持有时间
    缩小同步代码块范围,避免长时间持锁。
  2. 避免不必要的共享
    如果数据能做到线程私有,就不需要锁。
  3. 热点资源分片
    将一个全局锁拆分为多把锁,降低竞争概率。
  4. 替代方案
  • 在竞争激烈场景,考虑使用 ReentrantLock,它支持公平锁、可中断、超时等特性;
  • 在读多写少场景,可以用 ReadWriteLockStampedLock 提升并发度。

六、总结

  • 偏向锁:几乎零开销,适合无竞争场景;
  • 轻量级锁:利用 CAS 自旋,适合少量竞争;
  • 重量级锁:基于操作系统互斥量,适合激烈竞争;
  • 锁升级:无锁 → 偏向 → 轻量级 → 重量级,不会降级。
相关推荐
無限進步D5 小时前
Java 运行原理
java·开发语言·入门
難釋懷5 小时前
安装Canal
java
是苏浙5 小时前
JDK17新增特性
java·开发语言
不光头强5 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp8 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多8 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood8 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员9 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai