目录
一、升级的原因
我们可能在学习的过程中有听到过synchronized锁升级这句话,那么为什么要进行锁升级呢?
原因是因为,在Java1.5以前,synchronized还没有进行优化,一旦使用synchronized进行加锁之后,直接就是从无锁到重量级锁,而重量级锁的底层是依靠OS的Mutex Lock去实现互斥锁的功能的,Mutex是系统方法,在调用时需要从用户态转向内核态才能执行,消耗资源较大;并且,并不是加锁之后就一定是多线程并发访问被锁内容,因此就会造成资源的浪费。
所以,在Java1.6之后,JDK对synchronized实现了一些优化,引入了偏向锁和轻量级锁。
二、偏向锁
偏向锁(Biased Locking):是针对无竞争情况下的同步操作进行的优化手段。偏向锁在对象的对象头中的Mark Word字段中记录获取锁的线程ID。当一个线程获取了锁之后,如果再次进入同步块,就无需再进行加锁的操作,可以直接获取锁,避免了重复加锁解锁的开销。偏向锁的目标是优化无竞争场景下的性能表现。
三、轻量级锁
1.定义
轻量级锁(Lightweight Locking):轻量级锁是为了解决短时间内的竞争而引入的优化机制。当一个线程请求获取锁时,JVM会将对象头中的Mark Word字段复制到自己的线程栈帧中,并通过CAS(比较并交换)操作尝试将对象的状态从无锁状态转换为轻量级锁状态。如果竞争成功,则进入临界区执行,如果竞争失败,则升级为重量级锁。轻量级锁的目标是提供更快的锁获取和释放操作。
2.轻量级锁的加锁和解锁
加锁过程:
- 当一个线程尝试获取锁时,首先会在对象的Mark Word字段中记录锁的指针。如果该对象的锁状态是无锁状态(Unlocked)或者是偏向锁状态(Biased),那么线程可以直接使用CAS(比较并交换)操作将锁状态改为轻量级锁状态(Lightweight Locked)。这个转换过程是无锁的,不涉及线程之间的竞争。
- 如果CAS操作成功,表示该线程成功获取到了轻量级锁,它继续执行临界区的代码。
解锁过程:
- 当一个线程执行完临界区的代码后,准备释放轻量级锁时,它先使用CAS操作将对象的Mark Word字段恢复为无锁状态(Unlocked)。如果CAS操作成功,表示该线程成功释放了轻量级锁,其他线程可以竞争获取锁。
- 如果CAS操作失败,表示有其他线程正在竞争该锁,那么当前线程会膨胀为重量级锁(Heavyweight Lock)。这时,会在操作系统层面调用阻塞机制,将当前线程挂起,等待唤醒并重新竞争锁。
总的来说,轻量级锁的加锁和解锁过程是通过CAS操作来改变对象的锁状态,避免了阻塞等待的开销。如果CAS操作成功,线程成功获取/释放锁并继续执行;如果CAS操作失败,说明有竞争存在,线程会膨胀为重量级锁,进入阻塞状态等待唤醒。
四、重量级锁
重量级锁(Heavyweight Locking):重量级锁是Java中最基本、最传统的一种锁形式。当多个线程竞争同一个锁时,只有一个线程能够获取到锁,其他线程则被阻塞等待。当持有锁的线程执行完临界区的代码后,释放锁,被阻塞的线程中的一个会被唤醒并获取锁,继续执行。重量级锁的目标是保证多线程环境下的数据安全性。