在锁升级过程中,synchronized 锁的状态会从偏向锁、轻量级锁、重量级锁逐步升级,具体过程如下:
-
偏向锁(Biased Locking):
- 初始状态下,锁对象的标记为无锁状态。
- 当第一个线程进入同步块时,JVM会尝试将锁升级为偏向锁,并将该线程的ID记录在锁对象的头部。
- 如果其他线程试图访问同步块,偏向锁会被撤销,升级为轻量级锁。当持有偏向锁的线程执行完同步代码块后,JVM 会尝试将偏向锁恢复为无锁状态,并清除锁对象中记录的线程ID。这是为了确保在没有其他线程访问同步代码块的情况下,能够尽早释放锁对象,以便其他线程能够尽快获取到锁。
-
轻量级锁(Lightweight Locking):
- 当有第二个线程尝试获取同步块的锁时,偏向锁会被撤销,锁会升级为轻量级锁。
- JVM 会将当前线程尝试获取锁的对象头部信息复制到当前线程的栈帧中,并尝试使用 CAS(Compare and Swap)操作将对象头部信息修改为指向当前线程锁记录的指针。
- 如果 CAS 操作成功,则表示该线程成功获取了锁,继续执行同步代码块;否则,说明有其他线程也在竞争锁,轻量级锁会升级为重量级锁。
-
重量级锁(Heavyweight Locking):
- 当轻量级锁竞争失败,或者锁对象的锁记录存在多个线程竞争时,锁会升级为重量级锁。
- 重量级锁会导致竞争失败的线程进入阻塞状态,而不是忙等待,从而减少了竞争的消耗。
偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。
轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
重量级锁是将除了拥有锁的线程以外的线程都阻塞。
总的来说,synchronized 锁会根据竞争情况逐步升级为偏向锁、轻量级锁和重量级锁,从而保证了多线程环境下对共享资源的安全访问。
补充:
如何理解自旋
自旋是一种在等待某个条件满足时不释放 CPU 使用权的行为。在自旋过程中,线程会在原地循环等待,不停地检查条件是否满足,而不会进入阻塞状态或释放 CPU 使用权。因此,自旋会消耗 CPU 时间,但避免了线程进入和退出阻塞状态的开销,可以提高程序的响应速度和性能。
需要注意的是,自旋适用于等待时间较短、且预计条件很快就会满足的情况下。如果等待时间过长,自旋可能会导致 CPU 时间的浪费,影响系统的整体性能。因此,在使用自旋时需要谨慎考虑等待时间和自旋次数,以及合适的条件。