一、先整体了解一下重入锁ReentrantLock的大体思路?
先通过一张图,了解一下ReentrantLock的关系网(就好比看一个人NB不NB得先看他周围的人和家里的亲戚够不够NB,现在就开始看看ReentrantLock这个“家伙”有多牛逼!),先上图
- 首先ReentrantLock继承了他爸爸的AbstractQueuedSynchronizer的财产,这个人物有什么来历请看上一篇博客AbstractQueuedSynchronizer的原理实现
- 然后ReentrantLock还实现了他妈妈的一个目标(这里他爸爸和他妈妈本身没有太大的血缘关系)也就是ReentrantLock他实现的这个Lock接口和ReentrantLock继承的AbstractQueuedSynchronizer这个类本身没有太大关系,但是这都是他自己继承的财产他都可以随便用
- 而且ReentrantLock这个人自己手下还有几个子公司(也就是内部类),有一个母公司和两个子公司(这里指的是公平锁和非公平锁的两个字公司)Sync实现了AbstractQueuedSynchronizer的tryRelease方法。NonfairSync和FairSync两个类继承自Sync,实现了lock方法,然后分别公平抢占和非公平抢占针对tryAcquire有不同的实现。
二、开始梳理ReentrantLock具体实现的逻辑步骤
先看一下ReentrantLock类里面都有什么东西
二(1)重入锁中的公平锁的实现:
(1)首先公平锁对应的逻辑是 ReentrantLock 内部静态类 FairSync,看那一下静态内部类里面有什么
(2)会先从lock方法中去获取锁
final void lock() {
// 调用 AQS acquire 获取锁
acquire(1);
}
(3)这个acquire();方法是AQS中的方法,因为ReentrantLock继承的是AbstractQueuedSynchronizer.class这个类,而且FairSync这个又是ReentrantLock的内部类,所以就直接可以调用acquire()这个方法,(abstract static class Sync extends AbstractQueuedSynchronizer , ReentrantLock.FairSync.java重入锁中的内部类公平锁又是继承Sync 这个类(Sync .java是ReentrantLock.java中的内部类))
/**
* 这个方法也就是lock()方法的关键方法。tryAcquire获得资源,返回true,直接结束。若未获取资源,新建一个节点插入队尾,
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&//获取资源立刻结束
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//没有被中断过,也结束
selfInterrupt();
}
(4)然后是调用FairSync(公平锁中的)tryAcquire(arg)方法,去尝试再次去获取锁
1、解释这个方法是干嘛的hasQueuedPredecessors():
- 这个方法目的就是是判断是否有其他线程比当前线程在同步队列中等待的时间更长。有的话,返回 true,否则返回 false
- 具体解释就是:进入队列中会有一个队列可能会有多个正在等待的获取锁的线程节点,可能有Head(头结点)、Node1、Node2、Node3、Tail(尾节点),如果此时Node2节点想要去获取锁,在公平锁中他就会先去判断整个队列中是不是还有比我等待时间更长的节点,如果有,就让他先获取锁,如果没有,那我就获取锁(这里就体会到了公平性),
- 注意:因为这个Head(头结点)、Node1、Node2、Node3、Tail(尾节点)队列是现今先出队列FIFO的队列,也就是说谁先进来的谁就在前面,也即是谁先进来的等待时间肯定都会比后进来的等待时间较长
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//hasQueuedPredecessors()这个方法就是公平锁和非公平锁的不同之处之一
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {//没有前驱节点并且CAS设置成功
setExclusiveOwnerThread(current);//设置当前线程为独占线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//这里和非公平锁类似
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
二(2)重入锁中的非公平锁的实现:
(1)首先非公平锁对应的逻辑是 ReentrantLock 内部静态类 NoFairSync,看那一下静态内部类里面有什么
final void lock() {
/*
* 这里调用直接 CAS 设置 state 变量,如果设置成功,表明加锁成功。这里并没有像公平锁
* 那样调用 acquire 方法让线程进入同步队列进行排队,而是直接调用 CAS 抢占锁。抢占失败
* 再调用 acquire 方法将线程置于队列尾部排队。
*/
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
(2)也是去获取锁调用acquire()方法
public final void acquire(int arg) {
//
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
(3)然后再去调用tryAcquire(arg)方法,这个方法中和公平锁中的内容就会有些不一样了
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
(4)然后进入到nonfairTryAcquire()方法这里并没有调用hasQueuedPredecessors()这个方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取同步状态
int c = getState();
// 如果同步状态 c = 0,表明锁当前没有线程获得,此时可加锁。
if (c == 0) {
// 调用 CAS 加锁,如果失败,则说明有其他线程在竞争获取锁
if (compareAndSetState(0, acquires)) {
// 设置当前线程为锁的持有线程
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程已经持有锁,此处条件为 true,表明线程需再次获取锁,也就是重入
else if (current == getExclusiveOwnerThread()) {
// 计算重入后的同步状态值,acquires 一般为1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置新的同步状态值
setState(nextc);
return true;
}
return false;
}