synchronized 偏向锁偏向谁?

在 Java 中,synchronized 锁的实现包含三种状态:偏向锁、轻量级锁和重量级锁。偏向锁是一种优化机制,目的是为了减少同一线程重复获取锁的开销,尤其是在无竞争的情况下。偏向锁是“偏向”第一次获取锁的线程,以减少加锁和解锁的代价。

偏向锁的机制和偏向的对象

偏向锁阶段会偏向于第一个获取锁的线程。具体来说:

  1. 偏向锁的概念:偏向锁的主要目的是在单线程环境下减少锁操作的成本。默认情况下,偏向锁在 JVM 启动时是开启的(可以通过 -XX:+UseBiasedLocking 启用,JDK 15 后偏向锁已被弃用)。偏向锁的特点是锁定的对象会“偏向”于第一个获取该锁的线程。
  2. 偏向的对象:偏向锁并非立即执行加锁操作,而是将锁的“偏向”信息记录在对象头和当前线程中。当第一个线程访问加锁的代码块时,对象会偏向该线程。随后,同一线程再次进入该代码块时,不需要再次执行同步检查,直接进入临界区。

偏向锁的具体工作流程

偏向锁的实现依赖于对象头(Mark Word)中的标志位。当对象第一次被某个线程获取锁时,会将该线程的 ID 记录在对象头中,表示该对象已经“偏向”该线程。具体流程如下:

  1. 线程初次获取锁,设置偏向
    • 当一个线程首次尝试获取对象锁时,JVM 会检查对象头的状态位,如果该对象还没有被偏向(无锁状态),则将线程 ID 写入对象头,标记该线程对该对象拥有偏向锁。偏向锁的状态会标记在对象的 Mark Word 中。
    • 随后,同一线程再次尝试进入同步代码块时,不需要执行任何同步操作,因为对象已经偏向该线程了。这减少了锁重入的开销。
  2. 无锁竞争的场景,偏向锁保持
    • 偏向锁的优势在于无锁竞争的场景:只要该对象没有出现其他线程的锁竞争,偏向锁的线程可以不进行加锁和解锁操作而直接进入临界区。
    • 偏向锁的撤销只会在其他线程尝试获取该锁时才发生,而不会在同一线程中撤销。
  3. 偏向锁撤销
    • 当另一个线程尝试获取该偏向锁对象时,JVM 会撤销偏向锁,将其升级为轻量级锁。这个过程会暂停偏向锁持有线程,撤销偏向锁状态,恢复对象到无锁或轻量级锁的状态。
    • 偏向锁的撤销会触发一次 CAS(Compare-And-Swap)操作,将锁升级为轻量级锁,以便支持多线程的并发访问。

偏向锁的 Mark Word 结构

在偏向锁状态下,对象头的 Mark Word 会记录以下信息:

  • 锁标志位:标记当前锁的状态(偏向锁、轻量级锁、重量级锁等)。
  • 偏向标志位:标记对象是否是偏向锁状态。
  • 线程 ID:保存偏向锁持有线程的 ID。

偏向锁状态下的 Mark Word 结构如下:

偏向锁标志锁标志线程 ID (或偏向线程 ID)
101记录偏向的线程 ID

偏向锁的优缺点

优点

  • 在无锁竞争的情况下,偏向锁能显著减少锁的获取和释放的开销,因为它避免了 CAS 操作。
  • 偏向锁适合于线程局部使用的锁对象,例如大多数情况下由同一线程独占的锁对象。

缺点

  • 如果存在多个线程竞争锁,偏向锁会增加锁撤销的开销。在多线程高竞争环境下,偏向锁的撤销可能会导致性能下降。
  • 可以在高并发环境中通过 JVM 参数 -XX:-UseBiasedLocking 禁用偏向锁,从而直接使用轻量级锁。

举例说明偏向锁的工作流程

假设有以下代码片段:

javaCopy codepublic class Example {
    private final Object lock = new Object();

    public void synchronizedMethod() {
        synchronized (lock) {
            // 临界区代码
        }
    }
}

偏向锁流程示例

  1. 首次加锁:当线程 T1 首次进入 synchronizedMethod 时,lock 对象的状态由无锁变为偏向锁状态。JVM 将 T1 的线程 ID 写入 lock 对象的 Mark Word 中,表示此对象偏向 T1
  2. 重复加锁:如果 T1 再次进入 synchronizedMethod,因为 lock 已经偏向 T1,它可以直接进入临界区,而不需要执行任何同步操作。
  3. 其他线程竞争:如果线程 T2 尝试进入 synchronizedMethod,JVM 将撤销 lock 的偏向锁,将其升级为轻量级锁。偏向锁的撤销涉及 CAS 操作,并暂停 T1 线程以更新对象头信息。
  4. 进一步的竞争:如果锁的竞争持续,轻量级锁可能进一步升级为重量级锁,导致进入重量级锁的监视器锁定机制。

总结

偏向锁通过偏向于首次获取锁的线程,避免了不必要的加锁和解锁操作,极大提高了无锁竞争情况下的性能。但是在多线程并发环境中,如果偏向锁被频繁撤销,会带来性能损耗,因此在高竞争环境下通常不适用。偏向锁的设计适合于轻量级、无竞争的锁场景。

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x