在 Java 中,synchronized
关键字使用的锁有状态的升级和降级过程,主要涉及偏向锁、轻量级锁和重量级锁。
一、偏向锁(Biased Locking)
-
引入目的:
- 在没有竞争的情况下,减少锁获取的开销。很多情况下,一个对象在被一个线程使用时,不存在多线程竞争,此时使用偏向锁可以避免不必要的同步操作。
-
工作原理:
- 当一个线程首次访问同步块并获取锁时,锁对象会偏向这个线程,将线程 ID 记录在锁对象的头部。
- 后续这个线程再次进入同步块时,只需要检查锁对象的头部是否记录了自己的线程 ID,如果是,则直接进入同步块,无需进行任何同步操作。
二、轻量级锁(Lightweight Locking)
-
升级场景:
- 当有第二个线程竞争锁时,偏向锁会升级为轻量级锁。
-
工作原理(CAS+自旋):
- 轻量级锁使用 CAS(Compare and Swap)操作来尝试获取锁。线程在自己的栈帧中创建一个锁记录(Lock Record),并将锁对象的 Mark Word 复制到锁记录中。
- 然后线程尝试使用 CAS 将锁对象的 Mark Word 更新为指向自己的锁记录的指针。如果 CAS 操作成功,线程就获得了锁;如果失败,说明有其他线程在竞争锁,当前线程会进行自旋等待,即不断地尝试获取锁,而不是进入阻塞状态。
三、重量级锁(Heavyweight Locking)
-
升级场景:
- 如果轻量级锁的自旋等待时间过长或者自旋次数超过一定限制,轻量级锁会升级为重量级锁。
-
工作原理:
- 重量级锁会导致线程进入阻塞状态,等待操作系统的调度。当锁被释放时,操作系统会唤醒等待的线程。
- 重量级锁的开销比较大,因为涉及到线程的阻塞和唤醒,以及操作系统的调度。
四、锁的降级
-
情况较少:
- 锁的降级相对来说比较少见。一般在一些特定的场景下,比如在长时间没有竞争的情况下,可能会从重量级锁降级为轻量级锁或者偏向锁。
-
实现困难:
- 在实际应用中,由于锁的升级过程是自动的,而降级过程相对复杂且不太容易自动触发,通常需要开发者根据具体情况进行特殊的处理和优化。
五、总结
锁的升级和降级过程是 JVM 为了在不同的竞争情况下提高性能而自动进行的调整。在实际开发中,开发者一般不需要直接干预这个过程,但了解这个机制可以帮助我们更好地理解 Java 中多线程同步的性能特点和优化方向。同时,在高并发场景下,合理地使用同步机制和避免过度竞争是提高程序性能的关键。