从字节码与对象头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支持四种锁状态,按性能由高到低依次为:- 无锁
- 偏向锁(Biased Locking)
- 轻量级锁(Thin Lock / Non-migrating Lock)
- 重量级锁(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操作 | 短暂竞争 | | 重量级锁 | 最高开销,涉及系统调用 | 长期竞争,多线程并发 |
五、总结
从偏向锁升级为重量级锁的过程如下:
- 无锁状态 → 第一线程获取锁 → 偏向锁
- 偏向锁 → 其他线程竞争 → 偏向锁撤销
- 偏向锁撤销 → 尝试CAS获取锁 → 轻量级锁
- 轻量级锁 → 多次失败或长期占用 → 重量级锁
关键点:
偏向锁适用于单线程访问,轻量级锁适用于短暂竞争,重量级锁适用于长期竞争。
锁升级是一个不可逆的过程,一旦升级为重量级锁,就无法再降级。
六、优化建议
- 避免锁竞争:合理设计业务逻辑,减少锁粒度
- 使用更细粒度锁:如
ReentrantLock替代synchronized - 禁用偏向锁(适用于高并发场景):
-XX:-UseBiasedLocking
如需进一步分析特定场景下的锁状态,可提供具体代码片段或JVM日志。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报