synchronized

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`性能。理解这一机制有助于编写高效并发代码,避免不必要的锁竞争。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容