Administrator
发布于 2025-02-18 / 11 阅读
0
0

synchronized加锁的过程

synchronized加锁的过程本质是通过 monitorenter指令对象头(Mark Word)对象监视器(Monitor) 三者协作实现的。以下是它们之间的关联及详细过程:


1. 核心组件关系

组件

作用

关联场景

monitorenter指令

触发锁的获取(通过字节码解释或JIT编译后的机器指令)

所有锁操作入口

对象头(Mark Word)

存储当前锁状态(无锁、偏向锁、轻量级锁、重量级锁)和锁相关数据

锁状态判断与升级

对象监视器(Monitor)

管理锁的竞争、线程阻塞与唤醒(仅在重量级锁阶段启用)

重量级锁的线程同步


2. 加锁详细过程

当线程执行到synchronized代码块时,monitorenter指令触发以下流程:

步骤1:检查对象头(Mark Word)

  • JVM首先读取对象的 Mark Word,根据锁标志位(Lock Flag)判断当前锁状态:

    • 无锁(01):尝试通过CAS操作将Mark Word更新为偏向锁或轻量级锁。

    • 偏向锁(01):检查Mark Word中的线程ID是否为当前线程。

    • 轻量级锁(00):通过CAS自旋尝试获取锁。

    • 重量级锁(10):直接进入Monitor的竞争流程。

步骤2:锁升级与Mark Word修改

根据锁状态执行不同操作:

场景1:无锁 → 偏向锁
  • 若对象未加锁(无锁状态),JVM尝试通过CAS将 线程ID 写入Mark Word,并设置偏向锁标志。

  • 成功:线程直接进入同步代码(无需额外操作)。

  • 失败:撤销偏向锁,升级为轻量级锁。

场景2:偏向锁 → 轻量级锁
  • 若当前线程是偏向锁持有者:直接执行同步代码(可重入)。

  • 若其他线程竞争:

    1. 撤销偏向锁:在全局安全点暂停原持有线程,恢复Mark Word为无锁状态。

    2. 升级为轻量级锁:在栈帧中创建锁记录(Lock Record),将Mark Word拷贝到锁记录。

    3. 通过CAS将对象头的Mark Word替换为指向锁记录的指针(锁记录地址 + 轻量级锁标志00)。

场景3:轻量级锁 → 重量级锁
  • 若CAS自旋失败(竞争激烈):

    1. 锁膨胀:JVM分配一个Monitor对象,并将Mark Word指向Monitor的地址(锁标志变为10)。

    2. 关联Monitor:Mark Word中的指针指向Monitor,后续锁操作由Monitor管理。

步骤3:重量级锁与Monitor操作

  • 当锁升级为重量级锁时,Monitor正式介入:

    1. Monitor结构:包含以下关键字段:

      • Owner:持有锁的线程。

      • EntryList:竞争锁的阻塞线程队列。

      • WaitSet:调用wait()的等待线程队列。

    2. 获取锁

      • 线程尝试通过CAS将Monitor的Owner设置为自身。

      • 若失败,线程进入EntryList阻塞(通过操作系统的互斥量)。

    3. 释放锁:Owner线程退出同步代码时,重置Owner并唤醒EntryList中的线程。


3. 关键过程示例

示例1:轻量级锁的加锁

synchronized (obj) {
    // 代码块
}
  1. 线程A首次进入同步块:

    • Mark Word为无锁状态(01)。

    • CAS将Mark Word更新为轻量级锁(00),存储锁记录指针。

  2. 线程B竞争:

    • CAS失败,线程B自旋等待。

    • 自旋超过阈值后,锁膨胀为重量级锁,Mark Word指向Monitor。

示例2:重量级锁的Monitor竞争

synchronized (obj) {
    obj.wait(); // 调用wait()会释放锁,线程进入WaitSet
}
  1. 线程A持有Monitor的Owner字段。

  2. 调用wait()后,线程A释放Owner,进入WaitSet。

  3. 线程B从EntryList中被唤醒,成为新的Owner。


4. 三者的关联总结

阶段

monitorenter指令作用

对象头(Mark Word)变化

Monitor角色

无锁/偏向锁

触发CAS操作尝试获取锁

存储线程ID或锁记录指针

未启用

轻量级锁

触发自旋重试CAS

指向线程栈中的锁记录

未启用

重量级锁

触发Monitor的EntryList竞争

指向Monitor对象地址

管理Owner、EntryList、WaitSet


5. 性能与设计意义

  • 锁升级机制:通过动态调整锁状态(偏向→轻量→重量),在无竞争时减少开销,高竞争时保证正确性。

  • Monitor的代价:重量级锁依赖操作系统互斥量,涉及用户态到内核态切换,性能较低。

  • 优化建议:减少锁竞争(如缩小同步块)、使用java.util.concurrent工具类替代。


总结

synchronized的加锁过程是 monitorenter指令触发锁状态的判断与升级,通过修改对象头(Mark Word)记录锁信息,最终在重量级锁阶段由Monitor管理线程同步。这一机制在保证线程安全的同时,通过锁升级策略平衡了性能与可靠性。


评论