synchronized 是 Java 中最基础的同步机制,其底层通过锁升级(Lock Escalation)机制优化性能,根据竞争激烈程度动态调整锁的粒度,从无锁逐步升级为偏向锁、轻量级锁,最终可能升级为重量级锁。以下是完整的锁升级过程及原理详解。
一、锁升级的核心背景
synchronized 的锁升级是为了解决不同竞争场景下的性能问题:
- 无竞争或低竞争:通过轻量级机制(如偏向、CAS)减少同步开销。
- 高竞争:升级为重量级锁(内核级互斥),避免用户态频繁竞争导致的性能损耗。
二、锁升级的四个阶段
锁升级是单向不可逆的过程(无锁 → 偏向锁 → 轻量级锁 → 重量级锁),每个阶段的触发条件和底层实现不同。
1. 无锁状态(No Lock)
- 状态特征:对象头中无锁标记(
Mark Word的锁标志位为01,表示无锁)。 - 行为:任何线程均可自由访问同步块,无需同步操作。
- 触发升级条件:当第一个线程进入同步块时,尝试获取偏向锁,触发无锁→偏向锁的升级。
2. 偏向锁(Biased Lock)
- 核心目标:优化同一线程多次进入同一同步块的场景(如循环中重复加锁)。
- 状态特征:
Mark Word的锁标志位为01,偏向标志位(Biased)为1。- 记录当前持有锁的线程 ID(
Thread ID)。
- 加锁逻辑:
当线程首次进入同步块时,JVM 检查对象头的Mark Word:- 若未偏向(无锁或已撤销偏向),则通过 CAS 操作将
Mark Word的线程 ID 设置为当前线程,标记为偏向锁(无需原子操作,因首次设置无竞争)。 - 若已偏向当前线程(线程 ID 匹配),则直接进入同步块(无需任何同步操作)。
- 若未偏向(无锁或已撤销偏向),则通过 CAS 操作将
- 解锁逻辑:
偏向锁的解锁无需原子操作(因仅当前线程持有),直接清除Mark Word中的线程 ID 和偏向标志。 - 触发升级条件:
当其他线程尝试进入同步块时(即检测到Mark Word中的线程 ID 与当前线程不一致),偏向锁被撤销(Unbias),升级为轻量级锁。
3. 轻量级锁(Lightweight Lock)
- 核心目标:优化低竞争场景(多个线程交替获取锁,但竞争不激烈)。
- 状态特征:
Mark Word的锁标志位为00(表示轻量级锁)。Mark Word存储的是指向线程栈中锁记录(Lock Record)的指针。
- 加锁逻辑(CAS 操作):
- 线程在栈中创建一个
Lock Record(记录锁对象的引用和状态)。 - 通过 CAS 操作将对象头的
Mark Word替换为Lock Record的指针(原子操作)。 - 若 CAS 成功,线程获得锁;若失败,说明存在竞争,进入锁膨胀流程。
- 线程在栈中创建一个
- 解锁逻辑:
线程通过 CAS 操作将对象头的Mark Word恢复为原始值(即释放锁)。若 CAS 失败(其他线程已修改),则触发锁膨胀。 - 触发升级条件:
当多个线程频繁竞争锁(CAS 失败次数超过阈值,如 10 次),轻量级锁膨胀为重量级锁。
4. 重量级锁(Heavyweight Lock)
- 核心目标:处理高竞争场景(多个线程激烈争夺锁,CAS 无法高效解决)。
- 状态特征:
Mark Word的锁标志位为10(表示重量级锁)。Mark Word存储指向Monitor对象的指针(Monitor是 JVM 内核级的锁结构)。
- 加锁逻辑:
线程通过monitorenter指令尝试获取Monitor锁:- 若
Monitor的Owner为空,线程成为Owner,获得锁。 - 若
Owner非空,线程进入EntryList(阻塞队列)等待。
- 若
- 解锁逻辑:
线程通过monitorexit指令释放Monitor锁:- 若
Owner是当前线程,释放锁并将Owner置空(或唤醒EntryList中的线程)。 - 若
Owner非当前线程(非法释放),抛出IllegalMonitorStateException。
- 若
- 触发降级条件:
重量级锁无法降级为轻量级或偏向锁(一旦升级为重量级,后续只能通过解锁恢复无锁状态)。
三、锁升级的底层实现细节
1. 对象头(Mark Word)的结构
Mark Word 是对象头的核心部分,存储锁状态、线程 ID、哈希码等信息。其结构随锁状态变化而变化(以 64 位 JVM 为例):
| 锁状态 | 锁标志位 | 偏向标志位 | 存储内容 |
|---|---|---|---|
| 无锁 | 01 | 0 | 哈希码(HashCode)、GC 分代年龄(Generational GC Age) |
| 偏向锁 | 01 | 1 | 偏向线程 ID、偏向标志位、GC 分代年龄 |
| 轻量级锁 | 00 | - | 指向栈中 Lock Record 的指针 |
| 重量级锁 | 10 | - | 指向 Monitor 对象的指针 |
2. 偏向锁的撤销(Unbias)
当其他线程尝试进入同步块时,JVM 会检测到偏向锁的线程 ID 与当前线程不一致,触发偏向锁撤销:
- 检查偏向锁的线程是否存活(通过
Thread对象的alive状态)。 - 若线程存活,通过
CAS操作清除Mark Word中的线程 ID 和偏向标志,升级为轻量级锁。 - 若线程不存活(已终止),直接清除偏向标记,升级为轻量级锁。
3. 轻量级锁的膨胀(Inflation)
当轻量级锁的 CAS 操作失败次数超过阈值(默认 10 次),JVM 会认为存在激烈竞争,触发膨胀:
- 创建
Monitor对象(存储在 JVM 的Monitor列表中)。 - 通过
monitorenter指令将Mark Word替换为Monitor指针,锁标志位改为10(重量级锁)。
四、锁升级的性能影响
| 锁类型 | 适用场景 | 性能特点 |
|---|---|---|
| 无锁 | 无线程竞争 | 无同步开销,性能最优。 |
| 偏向锁 | 单线程重复加锁(如循环) | 无需原子操作,性能接近无锁(仅需设置线程 ID)。 |
| 轻量级锁 | 低竞争(多线程交替加锁) | 通过 CAS 减少内核态切换,性能优于重量级锁。 |
| 重量级锁 | 高竞争(多线程激烈争夺) | 涉及内核态互斥(pthread_mutex),性能最差(但能保证正确性)。 |
五、总结
synchronized 的锁升级机制通过动态调整锁的粒度,在不同竞争场景下选择最优的同步策略:
- 无锁 → 偏向锁:优化单线程重复加锁。
- 偏向锁 → 轻量级锁:处理多线程低竞争场景(CAS 替代内核操作)。
- 轻量级锁 → 重量级锁:应对高竞争场景(内核级互斥保证正确性)。
理解锁升级过程有助于优化同步代码(如避免不必要的偏向锁撤销、减少高竞争场景的锁使用),从而提升应用性能。
1万+

被折叠的 条评论
为什么被折叠?



