JVM 锁自动升级机制详解

JVM 锁自动升级机制详解

JVM 中的锁升级是指 synchronized 锁从无锁状态逐步升级到重量级锁的过程,这是 Java 并发性能优化的核心机制之一。Java 8 的 ConcurrentHashMap 使用的 synchronized 正是受益于这套自动升级机制。

1. 锁升级的四个阶段

JVM 锁状态按照竞争程度从低到高分为:

  1. 无锁状态 (Unlocked)

  2. 偏向锁 (Biased Locking)

  3. 轻量级锁 (Lightweight Locking)

  4. 重量级锁 (Heavyweight Locking)

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

2. 各阶段详细机制

2.1 偏向锁 (Biased Locking)

适用场景:没有实际竞争或只有单线程访问

实现原理

  • 在对象头 Mark Word 中记录偏向线程ID
  • 后续进入时只需检查线程ID是否匹配
  • 不涉及 CAS 操作和操作系统互斥

升级触发条件

  • 另一个线程尝试获取锁(产生竞争)

优势

  • 完全无同步开销(仅第一次有开销)
  • 适合 ConcurrentHashMap 的单桶无竞争情况

2.2 轻量级锁 (Thin Lock)

适用场景:低竞争、短时间同步

实现原理

  1. 在栈帧中建立锁记录(Lock Record)

  2. 使用 CAS 将对象头 Mark Word 替换为指向锁记录的指针

    assembly 复制代码
    cmpxchg  // CAS指令
  3. 成功则获取锁,失败则膨胀为重量级锁

升级触发条件

  • CAS 操作失败(表示有竞争)
  • 自旋超过阈值(默认10次,JVM可调节)

优势

  • 避免直接进入重量级锁
  • 在用户态完成同步(无需内核介入)

2.3 重量级锁 (Heavyweight Lock)

适用场景:高竞争场景

实现原理

  • 通过操作系统的互斥量(mutex)实现
  • 未获取锁的线程进入等待队列
  • 涉及用户态到内核态的切换

特点

  • 性能开销大(约100ns级延迟)
  • 保证公平性和可靠性

3. 锁升级全过程示例

以 ConcurrentHashMap 的桶锁为例:

java 复制代码
synchronized(bucketHead) {  // 桶头节点作为锁对象
    // 同步代码块
}
  1. 首次进入

    • 启用偏向锁,记录当前线程ID到对象头
  2. 同一线程再次进入

    • 检查线程ID匹配,直接执行(零开销)
  3. 第二个线程进入

    • 撤销偏向锁
    • 升级为轻量级锁(CAS竞争)
  4. 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 实现)

cpp 复制代码
// 偏向锁撤销
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 调优参数

  1. 关闭偏向锁(高竞争场景):

    bash 复制代码
    -XX:-UseBiasedLocking
  2. 调整自旋次数:

    bash 复制代码
    -XX:PreBlockSpin=10
  3. 启用自适应性自旋:

    bash 复制代码
    -XX:+UseSpinning

6. 在 ConcurrentHashMap 中的应用

  1. 理想情况

    • 多数桶无竞争 → 保持偏向锁状态
    • 读操作完全无锁
  2. 中等竞争

    • 少量线程操作同一桶 → 轻量级锁
    • 通过CAS快速解决冲突
  3. 高竞争

    • 热点桶出现 → 升级重量级锁
    • 但仅限于单个桶不影响其他桶操作

7. 现代JVM的优化趋势

  1. 锁消除 (Lock Elision):

    • 逃逸分析确定对象不会逃逸时,直接消除锁
  2. 锁粗化 (Lock Coarsening):

    • 合并相邻的同步块减少锁开销
  3. 自适应自旋

    • 根据历史成功率动态调整自旋时间

这种自动升级机制使得:

  • 无竞争时达到近乎无锁的性能
  • 低竞争时保持高效
  • 高竞争时保证正确性
  • 完美适配 ConcurrentHashMap 的桶锁需求
相关推荐
小灰灰搞电子30 分钟前
Qt 信号槽的扩展知识
开发语言·qt
nightunderblackcat38 分钟前
进阶向:Python图像处理,使用PIL库实现圆形裁剪
开发语言·图像处理·python
Kiri霧1 小时前
细谈kotlin中缀表达式
开发语言·微信·kotlin
想要成为祖国的花朵1 小时前
Java_Springboot技术框架讲解部分(二)
java·开发语言·spring boot·spring
Q_Q5110082851 小时前
python的小学课外综合管理系统
开发语言·spring boot·python·django·flask·node.js
勤奋的知更鸟1 小时前
JavaScript 性能优化实战:深入性能瓶颈,精炼优化技巧与最佳实践
开发语言·javascript·性能优化
hqxstudying2 小时前
Java行为型模式---模板方法模式
java·开发语言·设计模式·代码规范·模板方法模式
weixin_443290692 小时前
【脚本系列】如何使用 Python 脚本对同一文件夹中表头相同的 Excel 文件进行合并
开发语言·python·excel
荷蒲3 小时前
【小白量化智能体】应用5:编写通达信股票交易指标及生成QMT自动交易Python策略程序
开发语言·python
ljh5746491193 小时前
PHP password_verify() 函数
开发语言·php