Java中的锁升级过程与synchronized实现原理详解
字数 1372 2025-11-21 10:10:13

Java中的锁升级过程与synchronized实现原理详解

1. 知识点描述
锁升级是synchronized关键字在JDK 6之后引入的重要优化机制,它通过从无锁状态逐步升级到重量级锁的方式,在不同竞争条件下采用不同的锁策略,有效平衡了性能开销和线程安全。理解这个过程需要掌握对象头结构、锁状态变迁以及底层实现原理。

2. 对象头结构(锁的存储基础)

  • 每个Java对象在堆内存中都包含对象头,其中Mark Word是锁实现的关键
  • Mark Word结构(以64位系统为例):
    • 无锁状态:25位哈希码 + 4位分代年龄 + 1位偏向模式(0) + 2位锁标志(01)
    • 偏向锁:54位线程ID + 2位Epoch + 1位分代年龄 + 1位偏向模式(1) + 2位锁标志(01)
    • 轻量级锁:62位指向栈中锁记录的指针 + 2位锁标志(00)
    • 重量级锁:62位指向监视器Monitor的指针 + 2位锁标志(10)

3. 锁升级的完整过程

步骤1:无锁状态

  • 对象刚创建时处于无锁状态
  • 当第一个线程访问同步代码块时,检查锁标志位为01,准备进入锁状态

步骤2:偏向锁(单线程优化)

  • 目标:消除无竞争情况下的同步开销
  • 具体过程:
    1. 线程检查对象头中的偏向模式位
    2. 如果为可偏向(匿名偏向状态),通过CAS操作将线程ID写入Mark Word
    3. 成功后,该线程再次进入同步块时只需检查线程ID是否匹配
    4. 匹配成功则直接执行,无需任何同步操作
  • 优势:只有第一次需要CAS操作,后续几乎零开销

步骤3:轻量级锁(多线程交替执行)

  • 触发条件:当有第二个线程尝试获取锁时,发生锁竞争
  • 具体过程:
    1. 在安全点暂停拥有偏向锁的线程
    2. 检查原持有线程是否存活或已退出同步块
    3. 如果已退出,撤销偏向锁恢复到无锁状态
    4. 如果仍在执行,升级为轻量级锁:
      • 在当前线程的栈帧中创建锁记录(Lock Record)
      • 通过CAS将对象头的Mark Word复制到锁记录中
      • 然后用CAS将对象头替换为指向锁记录的指针
  • 轻量级锁通过CAS自旋尝试获取锁,适用于线程交替执行的场景

步骤4:重量级锁(激烈竞争)

  • 触发条件:轻量级锁自旋失败(默认自旋10次或自适应自旋)
  • 具体过程:
    1. 锁膨胀:创建ObjectMonitor对象(重量级锁的核心)
    2. 将对象头Mark Word指向ObjectMonitor
    3. 竞争线程进入等待队列,从用户态切换到内核态
    4. 依赖操作系统的互斥量实现线程阻塞和唤醒
  • 特点:真正的互斥锁,开销大但能应对高并发场景

4. 锁的降级机制

  • 重量级锁不会降级到轻量级锁(设计选择)
  • 但存在批量重偏向和批量撤销优化:
    • 当一类对象的偏向锁撤销次数超过阈值(默认20次)
    • JVM会认为该类不适合使用偏向锁,后续对象直接使用轻量级锁

5. 实际应用示例

public class LockUpgradeExample {
    private final Object lock = new Object();
    private int count = 0;
    
    public void increment() {
        synchronized(lock) {  // 这里会发生完整的锁升级过程
            count++;
        }
    }
}
  • 第一次执行:无锁 → 偏向锁
  • 第二个线程进入:偏向锁 → 轻量级锁
  • 多个线程竞争:轻量级锁 → 重量级锁

6. 优化建议

  • 明确对象生命周期:短生命周期对象禁用偏向锁(-XX:-UseBiasedLocking)
  • 控制锁粒度:减小同步代码块范围
  • 避免不必要的同步:使用线程安全类或无锁编程
  • 监控锁竞争:通过JStack等工具分析锁状态

这个锁升级过程体现了JVM在运行时根据实际竞争情况动态优化的能力,是理解Java并发编程底层实现的重要基础。

Java中的锁升级过程与synchronized实现原理详解 1. 知识点描述 锁升级是synchronized关键字在JDK 6之后引入的重要优化机制,它通过从无锁状态逐步升级到重量级锁的方式,在不同竞争条件下采用不同的锁策略,有效平衡了性能开销和线程安全。理解这个过程需要掌握对象头结构、锁状态变迁以及底层实现原理。 2. 对象头结构(锁的存储基础) 每个Java对象在堆内存中都包含对象头,其中Mark Word是锁实现的关键 Mark Word结构(以64位系统为例): 无锁状态:25位哈希码 + 4位分代年龄 + 1位偏向模式(0) + 2位锁标志(01) 偏向锁:54位线程ID + 2位Epoch + 1位分代年龄 + 1位偏向模式(1) + 2位锁标志(01) 轻量级锁:62位指向栈中锁记录的指针 + 2位锁标志(00) 重量级锁:62位指向监视器Monitor的指针 + 2位锁标志(10) 3. 锁升级的完整过程 步骤1:无锁状态 对象刚创建时处于无锁状态 当第一个线程访问同步代码块时,检查锁标志位为01,准备进入锁状态 步骤2:偏向锁(单线程优化) 目标:消除无竞争情况下的同步开销 具体过程: 线程检查对象头中的偏向模式位 如果为可偏向(匿名偏向状态),通过CAS操作将线程ID写入Mark Word 成功后,该线程再次进入同步块时只需检查线程ID是否匹配 匹配成功则直接执行,无需任何同步操作 优势:只有第一次需要CAS操作,后续几乎零开销 步骤3:轻量级锁(多线程交替执行) 触发条件:当有第二个线程尝试获取锁时,发生锁竞争 具体过程: 在安全点暂停拥有偏向锁的线程 检查原持有线程是否存活或已退出同步块 如果已退出,撤销偏向锁恢复到无锁状态 如果仍在执行,升级为轻量级锁: 在当前线程的栈帧中创建锁记录(Lock Record) 通过CAS将对象头的Mark Word复制到锁记录中 然后用CAS将对象头替换为指向锁记录的指针 轻量级锁通过CAS自旋尝试获取锁,适用于线程交替执行的场景 步骤4:重量级锁(激烈竞争) 触发条件:轻量级锁自旋失败(默认自旋10次或自适应自旋) 具体过程: 锁膨胀:创建ObjectMonitor对象(重量级锁的核心) 将对象头Mark Word指向ObjectMonitor 竞争线程进入等待队列,从用户态切换到内核态 依赖操作系统的互斥量实现线程阻塞和唤醒 特点:真正的互斥锁,开销大但能应对高并发场景 4. 锁的降级机制 重量级锁不会降级到轻量级锁(设计选择) 但存在批量重偏向和批量撤销优化: 当一类对象的偏向锁撤销次数超过阈值(默认20次) JVM会认为该类不适合使用偏向锁,后续对象直接使用轻量级锁 5. 实际应用示例 第一次执行:无锁 → 偏向锁 第二个线程进入:偏向锁 → 轻量级锁 多个线程竞争:轻量级锁 → 重量级锁 6. 优化建议 明确对象生命周期:短生命周期对象禁用偏向锁(-XX:-UseBiasedLocking) 控制锁粒度:减小同步代码块范围 避免不必要的同步:使用线程安全类或无锁编程 监控锁竞争:通过JStack等工具分析锁状态 这个锁升级过程体现了JVM在运行时根据实际竞争情况动态优化的能力,是理解Java并发编程底层实现的重要基础。