文章目录

Java Synchronized锁的四种状态与升级降级过程

发布于 2026-05-10 06:19:23 · 浏览 15 次 · 评论 0 条

Java Synchronized锁的四种状态与升级降级过程

Java中的synchronized关键字用于实现线程同步,其底层通过锁机制保证线程安全。锁的状态会根据竞争情况动态变化,主要分为四种:无锁、偏向锁、轻量级锁、重量级锁。理解这些状态及转换过程,有助于优化程序性能。


一、四种锁状态详解

1. 无锁状态

  • 特点:对象未被任何线程持有,处于完全未加锁状态。
  • 适用场景:对象刚创建时或锁被完全释放后。
  • 获取方式:线程首次尝试获取锁时,若对象头标记为无锁,则直接获取。
  • 关键点:此时对象头中的锁标志位为01(二进制),表示无锁。

2. 偏向锁

  • 特点:锁会偏向于第一个获取它的线程,避免后续同线程重复加锁/解锁的开销。
  • 适用场景:只有一个线程访问同步代码块或方法时。
  • 获取方式:线程首次获取锁时,JVM将对象头中的线程ID设置为当前线程ID,并置偏向锁标志位为1
  • 关键点:若线程ID匹配,直接进入同步代码块;不匹配则触发偏向锁撤销。

3. 轻量级锁

  • 特点:通过CAS(Compare-And-Swap)操作实现,竞争不激烈时避免重量级锁的内核态切换。
  • 适用场景:多个线程交替访问同步代码块,但很少同时竞争。
  • 获取方式:线程尝试获取锁时,在当前线程栈帧中创建锁记录(Lock Record),通过CAS将对象头中的指针指向该记录。
  • 关键点:若CAS成功,获取轻量级锁;失败则自旋等待或升级为重量级锁。

4. 重量级锁

  • 特点:通过操作系统互斥量(Mutex)实现,线程竞争激烈时使用,会阻塞线程。
  • 适用场景:多个线程频繁竞争同一锁。
  • 获取方式:轻量级锁CAS失败后,升级为重量级锁,线程进入阻塞状态,由操作系统调度。
  • 关键点:锁标志位为10,线程需通过系统调用(如pthread_mutex_lock)获取锁。

二、锁的升级过程

锁的升级方向固定为:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,不可逆(仅偏向锁可降级)。

1. 无锁 → 偏向锁

  • 触发条件:线程首次获取锁,且JVM开启了偏向锁(默认JDK 6+开启)。
  • 操作步骤
    1. 线程检查对象头锁标志位,若为01(无锁)。
    2. JVM将对象头中的线程ID设置为当前线程ID,偏向锁标志位置1
    3. 线程直接进入同步代码块。

2. 偏向锁 → 轻量级锁

  • 触发条件:其他线程尝试获取偏向锁(线程ID不匹配)。
  • 操作步骤
    1. 触发偏向锁撤销,将对象头锁标志位改为00(无锁)。
    2. 线程尝试通过CAS获取轻量级锁(在当前线程栈帧创建锁记录)。
    3. 若CAS成功,获取轻量级锁;失败则升级为重量级锁。

3. 轻量级锁 → 重量级锁

  • 触发条件:CAS获取轻量级锁失败(多个线程同时竞争)。
  • 操作步骤
    1. 线程自旋一定次数(默认10次),尝试再次CAS。
    2. 若自旋后仍失败,升级为重量级锁:
      • 对象头锁标志位置10,指向重量级锁的Monitor。
      • 线程进入阻塞队列,由操作系统调度。

三、锁的降级过程

仅偏向锁支持降级,轻量级锁和重量级锁升级后不可降级。

1. 偏向锁降级

  • 触发条件:偏向锁撤销后,对象未被任何线程持有。
  • 操作步骤
    1. 线程释放锁时,若对象头线程ID与当前线程匹配,直接清除线程ID。
    2. 对象头恢复为无锁状态(锁标志位01)。

2. 轻量级锁/重量级锁的“伪降级”

  • 说明:轻量级锁和重量级锁升级后,不会自动降级。但可通过以下方式间接实现:
    • 轻量级锁:线程释放锁时,若锁记录未被其他线程竞争,可恢复为无锁状态(但通常直接进入偏向锁或无锁)。
    • 重量级锁:线程释放锁后,对象头恢复为无锁状态,但不会降级为轻量级锁(需重新竞争)。

四、状态转换流程图

graph TD A[无锁 状态] -->|首次获取| B(偏向锁) B -->|其他线程竞争| C(轻量级锁) C -->|CAS失败/自旋超时| D(重量级锁) D -->|线程释放| E[无锁 状态] B -->|线程释放| E C -->|线程释放| E

五、关键参数与优化建议

1. 偏向锁延迟

  • 参数-XX:BiasedLockingStartupDelay=0(默认延迟4秒,避免JVM启动时竞争)。
  • 作用:JVM启动后立即启用偏向锁,减少延迟。

2. 轻量级锁自旋次数

  • 参数-XX:PreBlockSpin=10(默认10次)。
  • 作用:控制CAS失败后的自旋次数,避免过多CPU消耗。

3. 优化建议

  • 减少锁竞争:尽量缩小同步代码块范围,使用final对象(偏向锁更稳定)。
  • 避免偏向锁撤销:若线程池线程复用,可禁用偏向锁(-XX:-UseBiasedLocking)。
  • 监控锁状态:通过jstackVisualVM查看锁状态,定位性能瓶颈。

通过理解锁的状态转换,可针对性地优化代码,避免不必要的锁升级,提升程序性能。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文