前言
在上文 AbstractQueuedSyncchronizer学习 中,对AQS进行了相应的学习,知道AQS是Java并发包的一个同步基础机制。该接口有几个常用的子类,本文章主要对其中最常用的子类ReentrantLock
类进行学习,其他子类后续再讲。
ReentrantLock源码学习
内部数据结构
1 | //可重入独占锁,实现了Lock接口 |
由类实现可知,该类持有一个Sync
对象,提供所有的同步机制。
构造函数
1 | public class ReentrantLock implements Lock, java.io.Serializable { |
ReentrantLock
类有两个构造函数,一个无参构造函数,默认提供一个非公平锁机制;一个boolean类型的构造函数,根据boolea值确实提供何种锁机制。
获取锁
对于ReentrantLock,使用过的同学应该都知道,它的通常用法为:
1 | try { |
ReentrantLock会保证 do something
在同一时间只有一个线程在执行这段代码。其余线程会被挂起,直到获取锁。由此可知,ReentrantLock实现的就是一个 独占锁
的功能:有且只有一个线程获取到锁,其余线程全部挂起,直到该拥有锁的线程释放锁,被挂起的线程被唤醒重新开始竞争锁。
lock源码
1 | /** |
由方法注释可知,ReentrantLock
为可重入锁,内部属性state
代表了加锁的次数。由源码可知,ReentrantLock获取锁的具体实现由其内部类Sync
的子类提供。内部类Sync
的子类有两种:FairSync(公平锁)和NonfairSync(非公平锁)。至于公平锁和非公平锁,唯一的区别是在获取锁的方式。(是直接去获取锁,还是进入队列排队)。下面分析ReentrantLock中公平锁的实现。
FairSync类中lock源码
1 | //FairSync类中的lock源码 |
调用到了AQS的acquire方法:
AQS中acquire源码
1 | //AQS中acquire源码 |
该方法的具体分析,可以查看 AQS源码学习。可知tryAcquire
方法是要由子类去实现的。
FairSync类中tryAcquire源码
1 | protected final boolean tryAcquire(int acquires) { //返回true,表示获取到锁 |
功能分析:
在方法内部首先获取AQS中的标志位state
,在前文已提到过,state
在AQS类的不同子类中,有不同的意义。ReentrantLock中,表示加锁的次数。如果state == 0
,说明还没有线程持有锁,因此会去直接获取锁,由于是公平锁实现,因此在获取锁之前还要先判断队列中是否有其他已排队的线程,如果没有的话,则尝试修改state
值,修改成功,表示获取锁成功,返回true
;如果state 不为 0
,表示已有线程持有了锁,因为 ReentrantLock为可重入锁
,因此判断当前线程是否为持有锁的线程,是的话,state值+1,返回true
。
hasQueuedPredecessors源码
1 | //return true if there is a queued thread preceding the |
至此,ReentrantLock获取锁的公平锁逻辑已分析完,非公平锁逻辑和公平锁逻辑差不多,只是少了排队的逻辑。下面学习一下释放锁的逻辑。
unlock源码
1 | public void unlock() { |
与获取锁的逻辑一样,内部实现同样由Sync
内部类提供具体实现。调用到了AQS中的release方法
AQS中release源码
1 | public final boolean release(int arg) { |
tryRelease
方法具体逻辑由子类实现。
Sync类tryRelease源码
1 | protected final boolean tryRelease(int releases) { |
因为是可重入锁,因此在state == 0
后,需要通知AQS无需再记录哪个线程正在获取锁。即把当前拥有锁的线程置为空。无论是公平锁还是非公平锁,释放锁的逻辑都相同。