Java中的`synchronized`关键字通过锁升级机制优化多线程竞争下的性能,其过程涉及偏向锁、轻量级锁和重量级锁的转换。以下是锁升级的详细过程:
### 1. **无锁状态**
- 对象初始化时处于**无锁状态**,此时对象头的Mark Word存储对象的哈希码、分代年龄等信息。
- 当首次有线程尝试获取锁时,JVM会根据竞争情况决定是否启用偏向锁。
---
### 2. **偏向锁(Biased Locking)**
- **目的**:减少无竞争场景下的同步开销(如单线程重复访问同步块)。
- **流程**:
1. **偏向模式启用**:线程首次获取锁时,通过CAS将Mark Word中的线程ID设置为当前线程,并进入偏向模式。
2. **重入检查**:后续该线程进入同步块时,直接检查线程ID是否匹配,无需同步操作。
- **撤销条件**:
- 当另一线程尝试获取锁时,JVM会**暂停持有偏向锁的线程**,检查其是否仍在同步块中:
- 若已退出:撤销偏向锁,升级为**轻量级锁**。
- 若未退出:直接升级为轻量级锁,由两个线程通过CAS竞争。
- **注意**:JDK 15后默认禁用偏向锁(`-XX:-UseBiasedLocking`),因维护偏向锁的开销在某些场景下不划算。
---
### 3. **轻量级锁(Thin Lock)**
- **目的**:在低竞争场景下,避免线程阻塞(通过自旋代替操作系统互斥)。
- **流程**:
1. **锁记录分配**:线程在栈帧中分配锁记录(Lock Record),存储对象头的Mark Word副本。
2. **CAS竞争**:尝试通过CAS将对象头的Mark Word替换为指向锁记录的指针:
- 成功:线程获得锁,Mark Word锁标志位变为轻量级锁(00)。
- 失败:触发自旋(循环尝试),若自旋超过阈值(或竞争加剧),升级为重量级锁。
- **自旋优化**:JDK 6引入自适应自旋(Adaptive Spinning),根据历史成功率动态调整自旋次数。
---
### 4. **重量级锁(Heavyweight Lock)**
- **目的**:处理高竞争场景,通过操作系统互斥量(mutex)阻塞线程,避免CPU空转。
- **流程**:
1. **锁膨胀**:当轻量级锁自旋失败,JVM将对象关联到监视器(Monitor),Mark Word指向Monitor地址(锁标志位10)。
2. **线程阻塞**:未获取锁的线程进入阻塞队列,由操作系统调度唤醒。
- **特点**:涉及用户态到内核态的切换,开销较大,但能有效减少CPU资源浪费。
---
### 锁升级示意图
```plaintext
无锁 → 偏向锁(无竞争) → 轻量级锁(低竞争) → 重量级锁(高竞争)
```
---
### 关键细节补充
- **Mark Word结构**:对象头中的Mark Word动态存储锁状态、GC信息等,不同锁状态下格式不同(如图)。
- **不可逆升级**:锁升级是单向的(如重量级锁无法降级),但对象释放锁后可能回到无锁状态。
- **安全点(SafePoint)**:偏向锁撤销需在JVM安全点暂停线程,可能影响性能。
- **适用场景**:
- **偏向锁**:单线程重复访问同步块。
- **轻量级锁**:线程交替执行,竞争短暂。
- **重量级锁**:高并发、长耗时同步操作。
---
### 示例代码说明
```java
public class LockExample {
private final Object lock = new Object();
public void method() {
synchronized (lock) { // 锁根据竞争情况逐步升级
// 同步代码块
}
}
}
```
### 总结
锁升级通过权衡不同竞争场景下的开销,优化`synchronized`性能。理解这一机制有助于编写高效并发代码,避免不必要的锁竞争。