Seal^_^ 2025-11-25 19:51 采纳率: 72.6%
浏览 3
已结题

从字节码与对象头Mark Word角度,详细描述synchronized从偏向锁升级为重量级锁的全过程,以及每个阶段的性能特征。

从字节码与对象头Mark Word角度,详细描述synchronized从偏向锁升级为重量级锁的全过程,以及每个阶段的性能特征。

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-25 20:00
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    字节码对象头Mark Word的角度,详细描述synchronized偏向锁升级为重量级锁的全过程,以及每个阶段的性能特征如下:


    一、基本概念回顾

    1. synchronized关键字的作用

    synchronized是Java中用于实现线程同步的关键字,它通过**Monitor(监视器)**机制控制对共享资源的访问。

    2. 对象头结构(Mark Word)

    在JVM中,每个对象都有一个对象头(Object Header),其中包含:

    • Mark Word:存储对象的哈希码、锁状态、线程ID等信息。
    • Class Pointer:指向类元数据的指针。
    • Array Length(如果是数组对象):数组长度。

    在不同锁状态下,Mark Word的内容会发生变化。


    二、synchronized的锁状态演变

    Java中的synchronized支持四种锁状态,按性能由高到低依次为:

    1. 无锁
    2. 偏向锁(Biased Locking)
    3. 轻量级锁(Thin Lock / Non-migrating Lock)
    4. 重量级锁(Heavyweight Lock / Monitor Lock)

    当多个线程竞争同一把锁时,锁会从偏向锁逐步升级为重量级锁


    三、从偏向锁升级为重量级锁的全过程

    1. 初始状态:无锁

    • Mark Word 中保存的是对象的哈希码(或0),没有锁信息。
    • 线程第一次获取锁时,会尝试偏向锁

    注意:默认情况下,偏向锁是开启的(可以通过JVM参数关闭)。


    2. 偏向锁(Biased Locking)

    • 触发条件:第一个线程获取锁。
    • Mark Word 内容
      • 存储当前线程ID
      • 锁标志位为 01
    • 性能特征
      • 极低开销,因为不需要CAS操作
      • 适合单线程访问的场景
      • 缺点:如果发生竞争,需要进行锁撤销

    代码示例

    public synchronized void method() {
        // 方法体
    }
    

    字节码

      public void method();
        flags: ACC_PUBLIC, ACC_SYNCHRONIZED
        Code:
          stack=0, locals=1, args_size=1
             0: aload_0
             1: monitorenter
             2: ...
             9: monitorexit
            10: goto 16
           13: astore_1
           14: aload_1
           15: athrow
    

    3. 偏向锁撤销(Biased Lock Revocation)

    • 触发条件:有其他线程尝试获取锁(即存在竞争)
    • 过程
      • JVM会检查当前锁是否处于偏向状态
      • 如果是,则将该锁撤销,并尝试升级为轻量级锁
      • 此时Mark Word中会保存指向轻量级锁的指针

    性能特征

    • 比无锁稍高,但比重量级锁低
    • 锁撤销代价较高,可能导致性能抖动

    4. 轻量级锁(Thin Lock)

    • 触发条件:多个线程竞争锁,但没有长期阻塞
    • Mark Word 内容
      • 指向当前持有锁的线程的栈帧地址(称为Displaced Mark Word
      • 锁标志位为 00
    • 性能特征
      • 较轻量,使用CAS操作来尝试获取锁
      • 适合短时间竞争的场景
      • 缺点:如果锁被长时间占用,会导致频繁CAS失败,进而升级为重量级锁

    代码示例(非显式加锁):

    synchronized (obj) {
        // 代码块
    }
    

    字节码

      public void test();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=1
             0: aload_0
             1: dup
             2: astore_1
             3: monitorenter
             4: ...
            10: monitorexit
            11: aload_1
            12: invokevirtual #2                  // Method java/lang/Object.toString:()Ljava/lang/String;
            15: pop
            16: goto 24
            19: astore_2
            20: aload_1
            21: monitorexit
            22: aload_2
            23: athrow
    

    5. 重量级锁(Heavyweight Lock)

    • 触发条件:轻量级锁多次失败,或者锁被长期占用
    • 过程
      • JVM将锁升级为重量级锁
      • Mark Word 中保存的是指向Monitor对象的指针
      • 线程进入等待队列(Entry List)
    • 性能特征
      • 最高开销,涉及系统调用(如park()/unpark()
      • 适用于长期竞争的场景
      • 缺点:上下文切换和调度成本高,影响性能

    代码示例

    public synchronized void longRunningMethod() {
        // 长时间运行的方法
    }
    

    四、各阶段性能对比

    | 锁状态 | 性能特征 | 适用场景 | |------------|----------------------------------|------------------------------| | 无锁 | 最快,无锁开销 | 单线程访问 | | 偏向锁 | 极低开销,适合单线程 | 单线程访问,无竞争 | | 轻量级锁 | 较低开销,CAS操作 | 短暂竞争 | | 重量级锁 | 最高开销,涉及系统调用 | 长期竞争,多线程并发 |


    五、总结

    偏向锁升级为重量级锁的过程如下:

    1. 无锁状态 → 第一线程获取锁 → 偏向锁
    2. 偏向锁 → 其他线程竞争 → 偏向锁撤销
    3. 偏向锁撤销 → 尝试CAS获取锁 → 轻量级锁
    4. 轻量级锁 → 多次失败或长期占用 → 重量级锁

    关键点
    偏向锁适用于单线程访问轻量级锁适用于短暂竞争重量级锁适用于长期竞争
    锁升级是一个不可逆的过程,一旦升级为重量级锁,就无法再降级。


    六、优化建议

    • 避免锁竞争:合理设计业务逻辑,减少锁粒度
    • 使用更细粒度锁:如ReentrantLock替代synchronized
    • 禁用偏向锁(适用于高并发场景):
      -XX:-UseBiasedLocking
      

    如需进一步分析特定场景下的锁状态,可提供具体代码片段或JVM日志。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 12月11日
  • 已采纳回答 12月3日
  • 创建了问题 11月25日