synconized锁的升级过程可以清晰地分为三个阶段:偏向锁、轻量级锁(自旋锁)和重量级锁。这个过程是为了优化锁的性能,减少不必要的同步开销。以下是详细的升级过程:
1. 偏向锁(Biased Locking)
目的:减少无竞争情况下的同步操作开销。
工作机制:
当一个线程首次访问同步代码块并获取对象的锁时,JVM会在对象的头部(Mark Word)中记录下该线程的ID,标记对象已偏向于该线程。
当该线程再次访问该同步代码块时,JVM会检查对象的头部信息,如果发现偏向的线程ID正是当前线程ID,则直接执行同步代码块,无需进行任何同步操作。
如果偏向的线程ID不是当前线程ID,或者对象未被偏向,则需要进行锁的撤销或升级。
2. 轻量级锁(Lightweight Locking)
目的:尽量避免线程切换的开销。
工作机制:
如果线程A尝试获取一个已经被线程B持有的偏向锁,JVM会尝试撤销偏向锁,升级为轻量级锁。
升级时,JVM会在当前线程的栈帧中创建一个Lock Record(锁记录),并将对象的Mark Word复制到Lock Record中。
然后,线程A会尝试使用CAS(Compare-and-Swap)操作将对象的Mark Word更新为指向Lock Record的指针。
如果CAS操作成功,则线程A获取到轻量级锁,继续执行同步代码块;如果CAS操作失败,则说明有其他线程(如线程B)正在持有该锁,此时线程A会进入自旋等待状态。
自旋等待过程中,线程A会不断尝试获取锁,直到成功或自旋次数达到上限。
3. 重量级锁(Heavyweight Locking)
目的:防止CPU空转,减少系统开销。
工作机制:
如果线程A在自旋等待过程中未能成功获取轻量级锁,或者等待时间超过阈值,JVM会将轻量级锁升级为重量级锁。
升级时,JVM会将未获取到锁的线程(如线程A)阻塞,使其进入等待队列(Wait Set)中等待。
同时,持有锁的线程(如线程B)在释放锁时,会唤醒等待队列中的线程进行竞争。
重量级锁通过操作系统的互斥量(Mutex)来实现,当线程尝试获取锁时,如果锁已被其他线程持有,则该线程会被阻塞,直到持有锁的线程释放锁。
总结
synchronized锁的升级过程是一个从偏向锁到轻量级锁再到重量级锁的渐进过程,旨在优化锁的性能和减少不必要的同步开销。在实际应用中,我们应该尽量避免出现重量级锁的情况,以减少线程切换和阻塞的开销。