重量级锁的基本原理
当轻量级锁膨胀到重量级锁之后,意味着线程只能被挂起阻塞来等待被唤醒了。
重量级锁的加锁的基本流程
任意线程对 Object(Object 由 synchronized 保护)的访问,首先要获得 Object 的监视器。如果获取失败,线程进入同步队列,线程状态变为 BLOCKED。当访问 Object 的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。
回顾线程的竞争机制
再来回顾一下线程的竞争机制对于锁升级这块的一些基本流程。方便大家更好的理解加入有这样一个同步代码块,存在 Thread#1、Thread#2 等多个线程
synchronized (lock) {
// do something
}
- 情况一:只有 Thread#1 会进入临界区;
- 情况二:Thread#1 和 Thread#2 交替进入临界区,竞争不激
烈; - 情况三:Thread#1/Thread#2/Thread3… 同时进入临界区,
竞争激烈
偏向锁
此时当 Thread#1 进入临界区时,JVM 会将 lockObject 的对象头 Mark Word 的锁标志位设为“01”,同时会用 CAS 操作把 Thread#1 的线程 ID 记录到 Mark Word 中,此时进入偏向模式。
所谓“偏向”,指的是这个锁会偏向于 Thread#1,
若接下来没有其他线程进入临界区,则 Thread#1 再出入临界区无需再执行任何同步操作。
也就是说,若只有Thread#1 会进入临界区,实际上只有 Thread#1 初次进入临界区时需要执行 CAS 操作,以后再出入临界区都不会有同步操作带来的开销。
轻量级锁
偏向锁的场景太过于理想化,更多的时候是 Thread#2 也会尝试进入临界区, 如果 Thread#2 也进入临界区但是Thread#1 还没有执行完同步代码块时,会暂停 Thread#1并且升级到轻量级锁。
Thread#2 通过自旋再次尝试以轻量级锁的方式来获取锁
重量级锁
如果 Thread#1 和 Thread#2 正常交替执行,那么轻量级锁基本能够满足锁的需求。但是如果 Thread#1 和 Thread#2同时进入临界区,那么轻量级锁就会膨胀为重量级锁,意味着 Thread#1 线程获得了重量级锁的情况下,Thread#2就会被阻塞