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+开启)。
- 操作步骤:
- 线程检查对象头锁标志位,若为
01(无锁)。 - JVM将对象头中的线程ID设置为当前线程ID,偏向锁标志位置
1。 - 线程直接进入同步代码块。
- 线程检查对象头锁标志位,若为
2. 偏向锁 → 轻量级锁
- 触发条件:其他线程尝试获取偏向锁(线程ID不匹配)。
- 操作步骤:
- 触发偏向锁撤销,将对象头锁标志位改为
00(无锁)。 - 线程尝试通过CAS获取轻量级锁(在当前线程栈帧创建锁记录)。
- 若CAS成功,获取轻量级锁;失败则升级为重量级锁。
- 触发偏向锁撤销,将对象头锁标志位改为
3. 轻量级锁 → 重量级锁
- 触发条件:CAS获取轻量级锁失败(多个线程同时竞争)。
- 操作步骤:
- 线程自旋一定次数(默认10次),尝试再次CAS。
- 若自旋后仍失败,升级为重量级锁:
- 对象头锁标志位置
10,指向重量级锁的Monitor。 - 线程进入阻塞队列,由操作系统调度。
- 对象头锁标志位置
三、锁的降级过程
仅偏向锁支持降级,轻量级锁和重量级锁升级后不可降级。
1. 偏向锁降级
- 触发条件:偏向锁撤销后,对象未被任何线程持有。
- 操作步骤:
- 线程释放锁时,若对象头线程ID与当前线程匹配,直接清除线程ID。
- 对象头恢复为无锁状态(锁标志位
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)。 - 监控锁状态:通过
jstack或VisualVM查看锁状态,定位性能瓶颈。
通过理解锁的状态转换,可针对性地优化代码,避免不必要的锁升级,提升程序性能。

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