ReentrantLock 源码解析
文章目录
- ReentrantLock 源码解析
- 前言
- 一、字段分析
- 二、内部类分析
- 1、Sync
- 2、FairSync
- 3、NonfairSync
- 三、方法分析
- 1、构造方法
- 2、其他方法
- 总结
前言
-
ReentrantLock 实现了 Lock 接口,内部基于 AQS 实现。所以想要弄懂 ReentrantLock ,一定要先弄明白 AQS。
-
ReentrantLock 是可重入锁,同一个线程能够多次获取到同一个 ReentrantLock 锁。
-
ReentrantLock 类采用的是 AQS 技术中的独占模式,实现了 AQS 的 3 个关键方法:tryAcquire(),tryRelease(),isHeldExclusively()方法。
- 一个线程可以多次通过 ReentrantLock 的 lock()方法获取锁,但是要想完全释放锁,就必须调用相同次数的 unlock()方法。
- ReentrantLock 对同一个线程的可重入性是通过内部的计数器实现的,同一个线程调用lock()方法加锁,计数器就会 + 1。
- 同一个线程调用 unlock()方法释放锁,计数器就会 - 1。
-
ReentrantLock 的内部,有两个抽象类:NonFairSync 和 FairSync,二者都继承了 Sync 类,而 Sync 类继承了 AQS 。
- NonFairSync :表示的是非公平模式。
- FairSync:表示的是公平模式。
-
ReentrantLock 和 AQS 的关系图:
一、字段分析
private final Sync sync:
ReentrantLock 只有这一个字段,代表了公平锁或者非公平锁,是 AQS 的子类,Sync 是ReentrantLock 内部真正用来操作的类。可以想象 ReentrantLock 只是一个壳,所有关于ReentrantLock 的操作,其实在 ReentrantLock 内部都是对 sync 进行操作的。
二、内部类分析
1、Sync
- state:继承自 AQS,在 ReentrantLock 中的含义为:
0:当前锁没有被任何线程持有。
1:当一个线程第一次获取该锁时,会尝试使用CAS设置state的值为1。
> 1:当一个线程获取锁后,再次调用 lock(),每次调用 state + 1。每次调用 unlock()state - 1,当减为 0 时,该线程释放了锁。
abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;//抽象的获取锁的方法,由子类实现abstract void lock();//非公平锁的获取锁方法//acquires:传入的是 state 值,传入的是 1final boolean nonfairTryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取状态值int c = getState();//状态值 = 0 ,说明当前锁没有被任何线程持有if (c == 0) {//使用 CAS 尝试将 state 设为 1if (compareAndSetState(0, acquires)) {//设置成功后,表示获取到了锁,将 锁持有的线程设置为当前线程setExclusiveOwnerThread(current);//加锁成功return true;}}//如果 state = 0,说明已经有线程持有锁了,判断是否是当前线程对象自己持有的else if (current == getExclusiveOwnerThread()) {//当前线程已经持有锁了,将 state + 1int nextc = c + acquires;//state 不停地 + 1,导致溢出了。重入次数过多if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置statesetState(nextc);//可重入获取锁成功return true;}//锁已经被其他线程持有,当前线程获取锁失败return false;}//释放锁//releases :传入的是 state,ReentranLock 释放锁传入的是 1protected final boolean tryRelease(int releases) {//计算释放锁后的state值int c = getState() - releases;//判断占用锁的是不是当前线程,如果不是,则报错if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();//记录是否释放成功 boolean free = false;//如果 state == 0,则释放锁成功if (c == 0) {free = true;//锁释放成功,将锁的持有线程设置为空setExclusiveOwnerThread(null);}//更新状态值setState(c);//返回是否释放成功return free;}//判断持有锁的线程是否为当前线程//true:持有锁的线程是当前线程//false:持有锁的线程不是当前线程protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}//创建ConditionObject,用来关联条件队列final ConditionObject newCondition() {return new ConditionObject();}//获取持有锁的线程final Thread getOwner() {return getState() == 0 ? null : getExclusiveOwnerThread();}//如果持有锁的线程为当前线程,返回 state,否则返回 0final int getHoldCount() {return isHeldExclusively() ? getState() : 0;}//判断锁是否空闲//true:空闲//false:已被获取final boolean isLocked() {return getState() != 0;}}
2、FairSync
- 公平锁的实现。
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;//获取锁final void lock() {acquire(1);}//获取公平锁protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取状态值int c = getState();//如果状态值为0,说明锁是空闲的,可以被获取if (c == 0) {//hasQueuedPredecessors():判断当前线程所在的节点(同步队列中)是否还有前驱节点//true:有,false:同步队列为空(还未创建同步队列获只有空的head节点)或当前节点为头结点(指的是head的next节点)//所以翻译为:没有前驱节点 且 使用cas方式设置 state 为 1,说明获取锁成功//因为如果有前驱节点,由于是公平锁,需要先加入到同步队列的队尾进行排队,下一个尝试获取资源的是头结点代表的资源if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {//获取锁资源成功,设置锁拥有的线程为当前线程setExclusiveOwnerThread(current);return true;}}//否则判断锁拥有的线程是否为当前线程,true说明是进行锁的重入操作,将 state + 1else if (current == getExclusiveOwnerThread()) {//计算 state,就是 state + 1int nextc = c + acquires;//判断是否溢出if (nextc < 0)throw new Error("Maximum lock count exceeded");//更新statesetState(nextc);return true;}//否则获取锁失败return false;}}
3、NonfairSync
-
非公平锁的实现。
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;//获取锁final void lock() {//使用cas的方式将state设置为1,成功说明锁未被持有,直接获取锁成功if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else//失败说明锁已经被持有,使用非公平的方式尝试获取锁acquire(1);}//尝试获取锁资源,采用的是非公平的方式protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}
三、方法分析
1、构造方法
//无参构造函数,默认使用的是非公平锁public ReentrantLock() {sync = new NonfairSync();}//有参构造函数//true:公平锁//false:非公平锁public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
2、其他方法
//获取锁,调用的其实是 sync 的方法(忽视中断)
public void lock() {sync.lock();}//获取锁,调用的其实是 sync 的方法(不忽视中断)
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}//尝试获取锁(非公平锁)
public boolean tryLock() {return sync.nonfairTryAcquire(1);}//尝试获取锁,获取失败后进入 doAcquireNanos 方法,如果时间超过了 timeout,则不进入同步队列了
public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}//释放锁,调用的其实是 sync 的方法(忽视中断)
public void unlock() {sync.release(1);}//创建 Condition ,其实调用的是sync的方法,创建是 AQS 的 ConditionObject 对象
public Condition newCondition() {return sync.newCondition();}//获取state,如果锁是空闲的则返回 0
public int getHoldCount() {return sync.getHoldCount();}//true:获取到锁的线程为当前线程
//false:获取到锁的线程不为当前线程
public boolean isHeldByCurrentThread() {return sync.isHeldExclusively();}//true:锁已被获取
//false:锁空闲
public boolean isLocked() {return sync.isLocked();}//方法都比较简单
...
总结
-
ReentrantLock 的代码比较简单,更多的实现在 AQS 中,结合 ReentrantLock 和 AQS 的源码,总结下ReentrantLock 获取锁和释放锁的整个流程。
-
公平模式下获取锁:lock()
-
非公平模式下获取锁:lock()
- 非公平模式下释放锁:unlock()和 公平模式下释放锁:unlock()执行方法一样的。
公平模式和非公平模式下获取锁的区别:体现在 lock 方法和 tryAcquire方法
-
公平模式下源码:
-
非公平模式下源码:
-
可以发现,
非公平锁模式下,如果锁是空闲状态可直接获取锁(lock方法中)。而公平方法中没有。并且在获取锁失败后,调用 tryAcquire 方法时,检查 state == 0 (说明锁是空闲的),非公平模式下是直接尝试获取锁
,而公平模式是检查同步队列中是否还有节点,有的话则不获取锁,没有才获取锁。