高 困难
偏向锁-轻量级锁-重量级锁#
一句话答案#
锁升级路径:无锁→偏向锁(Mark Word 记录线程 ID)→轻量级锁(CAS 竞争)→重量级锁(Monitor 阻塞),不可降级。
核心要点
锁升级过程:
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
(单线程) (短时竞争) (激烈竞争)plaintext| 锁状态 | 适用场景 | 实现方式 | 开销 |
|---|---|---|---|
| 偏向锁 | 只有一个线程访问 | Mark Word 记录线程 ID,后续无需 CAS | 几乎无 |
| 轻量级锁 | 多线程交替执行 | CAS 替换 Mark Word 为栈中 Lock Record | CAS 开销 |
| 重量级锁 | 多线程同时竞争 | Monitor(mutex),未获锁线程阻塞 | 线程阻塞/唤醒 |
升级触发条件:
- 偏向→轻量级:第二个线程尝试获取锁时
- 轻量级→重量级:CAS 自旋超过阈值(默认10次/自适应)
关键细节:
- JDK15 默认关闭偏向锁(
-XX:-UseBiasedLocking) - 锁只能升级不能降级(GC 时特殊情况除外)
- 偏向锁撤销需要等安全点(STW)
面试回答(2分钟版)
synchronized 的锁有一个从低到高的升级过程:无锁、偏向锁、轻量级锁、重量级锁,只能升级不能降级。偏向锁适用于始终只有一个线程访问同步块的场景,JVM 在对象头的 Mark Word 中记录线程 ID,后续该线程再进入时直接比对 ID 即可,几乎没有同步开销。当第二个线程尝试获取锁时,偏向锁撤销升级为轻量级锁,此时线程通过 CAS 将 Mark Word 替换为指向栈中 Lock Record 的指针来竞争锁,没拿到的线程自旋等待。如果自旋超过阈值(默认 10 次或自适应调整)仍未获取锁,就膨胀为重量级锁,底层使用操作系统的 mutex,未获锁的线程直接阻塞挂起,需要内核态切换,开销最大。值得注意的是 JDK 15 默认关闭了偏向锁,因为偏向锁撤销需要等待安全点会触发 STW,在现代多线程应用中弊大于利。理解锁升级机制有助于在编码时合理控制锁的粒度和竞争程度。
追问与易错
追问方向:
- “偏向锁撤销的代价?”→ 需要等待全局安全点(STW),暂停持有偏向锁的线程来撤销,在多线程竞争频繁时安全点开销显著
- “JDK15 为什么关闭偏向锁?”→ 现代应用多线程竞争普遍,偏向锁撤销的 STW 开销大于收益;偏向锁维护代码复杂增加了 JVM 维护成本,收益不再明显
- “Mark Word 在不同锁状态存什么?”→ 无锁存 hashcode+age+锁标志 01;偏向锁存线程 ID+epoch+age+标志 01;轻量级锁存指向栈中 Lock Record 的指针+标志 00;重量级锁存指向 Monitor 的指针+标志 10
易错点:
- ❌ 锁能降级——正常情况只升不降
- ❌ 偏向锁就是不加锁——有 CAS 设置线程 ID 的开销