JVM 锁自动升级机制详解
JVM 锁自动升级机制详解
JVM 中的锁升级是指 synchronized 锁从无锁状态逐步升级到重量级锁的过程,这是 Java 并发性能优化的核心机制之一。Java 8 的 ConcurrentHashMap 使用的 synchronized 正是受益于这套自动升级机制。
1. 锁升级的四个阶段
JVM 锁状态按照竞争程度从低到高分为:
- 无锁状态 (Unlocked)
- 偏向锁 (Biased Locking)
- 轻量级锁 (Lightweight Locking)
- 重量级锁 (Heavyweight Locking)
[无锁] → [偏向锁] → [轻量级锁] → [重量级锁]
2. 各阶段详细机制
2.1 偏向锁 (Biased Locking)
适用场景:没有实际竞争或只有单线程访问
实现原理:
- 在对象头 Mark Word 中记录偏向线程ID
- 后续进入时只需检查线程ID是否匹配
- 不涉及 CAS 操作和操作系统互斥
升级触发条件:
- 另一个线程尝试获取锁(产生竞争)
优势:
- 完全无同步开销(仅第一次有开销)
- 适合 ConcurrentHashMap 的单桶无竞争情况
2.2 轻量级锁 (Thin Lock)
适用场景:低竞争、短时间同步
实现原理:
- 在栈帧中建立锁记录(Lock Record)
- 使用 CAS 将对象头 Mark Word 替换为指向锁记录的指针
cmpxchg // CAS指令
- 成功则获取锁,失败则膨胀为重量级锁
升级触发条件:
- CAS 操作失败(表示有竞争)
- 自旋超过阈值(默认10次,JVM可调节)
优势:
- 避免直接进入重量级锁
- 在用户态完成同步(无需内核介入)
2.3 重量级锁 (Heavyweight Lock)
适用场景:高竞争场景
实现原理:
- 通过操作系统的互斥量(mutex)实现
- 未获取锁的线程进入等待队列
- 涉及用户态到内核态的切换
特点:
- 性能开销大(约100ns级延迟)
- 保证公平性和可靠性
3. 锁升级全过程示例
以 ConcurrentHashMap 的桶锁为例:
synchronized(bucketHead) { // 桶头节点作为锁对象// 同步代码块
}
-
首次进入:
- 启用偏向锁,记录当前线程ID到对象头
-
同一线程再次进入:
- 检查线程ID匹配,直接执行(零开销)
-
第二个线程进入:
- 撤销偏向锁
- 升级为轻量级锁(CAS竞争)
-
CAS竞争失败:
- 自旋重试(自适应自旋)
- 自旋失败后升级重量级锁
4. 关键技术实现
4.1 对象头结构
|---------------------------------------------------|
| Mark Word (64 bits) |
|---------------------------------------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
|---------------------------------------------------|
biased_lock
:偏向锁标志lock
:锁状态标志- 00:轻量级锁
- 01:无锁/偏向锁
- 10:重量级锁
- 11:GC标记
4.2 升级过程关键代码(HotSpot 实现)
// 偏向锁撤销
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, TRAPS) {if (UseBiasedLocking) {BiasedLocking::revoke(obj, THREAD);}slow_enter(obj, lock, THREAD);
}// 轻量级锁获取
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {markOop mark = obj->mark();if (mark->is_neutral()) { // 无锁状态lock->set_displaced_header(mark);if (mark == obj()->cas_set_mark(markOopDesc::INFLATING(), mark)) {// 膨胀为重量级锁ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);}}
}
5. 性能影响与优化
5.1 各阶段典型耗时
锁阶段 | 耗时 | 特点 |
---|---|---|
偏向锁 | ~1-2ns | 仅第一次有开销 |
轻量级锁 | ~5-10ns | CAS操作开销 |
重量级锁 | ~100ns+ | 系统调用/上下文切换 |
5.2 JVM 调优参数
- 关闭偏向锁(高竞争场景):
-XX:-UseBiasedLocking
- 调整自旋次数:
-XX:PreBlockSpin=10
- 启用自适应性自旋:
-XX:+UseSpinning
6. 在 ConcurrentHashMap 中的应用
-
理想情况:
- 多数桶无竞争 → 保持偏向锁状态
- 读操作完全无锁
-
中等竞争:
- 少量线程操作同一桶 → 轻量级锁
- 通过CAS快速解决冲突
-
高竞争:
- 热点桶出现 → 升级重量级锁
- 但仅限于单个桶不影响其他桶操作
7. 现代JVM的优化趋势
-
锁消除 (Lock Elision):
- 逃逸分析确定对象不会逃逸时,直接消除锁
-
锁粗化 (Lock Coarsening):
- 合并相邻的同步块减少锁开销
-
自适应自旋:
- 根据历史成功率动态调整自旋时间
这种自动升级机制使得:
- 无竞争时达到近乎无锁的性能
- 低竞争时保持高效
- 高竞争时保证正确性
- 完美适配 ConcurrentHashMap 的桶锁需求