基于 AQS 分析 Condition


崇高的理想就像生长在高山上的鲜花,如果要摘下它,勤奋才能是攀登的绳索



代码案例

public class ReentrantLockConditionDemo {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
new Thread(() -> {
reentrantLock.lock();
Thread.currentThread().setName("线程一");
System.out.println(Thread.currentThread().getName() + "执行");
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "执行 await 方法,等待线程二执行完成");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}).start();

try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}

new Thread(() -> {
reentrantLock.lock();
Thread.currentThread().setName("线程二");
System.out.println(Thread.currentThread().getName() + "执行");
try {
System.out.println(Thread.currentThread().getName() + "执行完成,执行signal方法唤醒线程一");
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}).start();

}
}

结果输出

基于 AQS 分析 Condition_后端

源码分析

分析一下构造函数

ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();

看看这句话里面是什么 reentrantLock.newCondition();

public Condition newCondition() {
return sync.newCondition();
}

一看到sync 又是AQS中东西了,ConditionObject 这个类是AQS中的内部类,但是这个newCondition()方法和newCondition()方法都是ReentrantLock中定义的方法

final ConditionObject newCondition() {
return new ConditionObject();
}

这个new出来的对象的是类就是AQS中的内部类了

public abstract class AbstractQueuedSynchronizer{
public class ConditionObject implements Condition{
public ConditionObject() { }
}}

这里面省略了一些东西,不过关系就是这个关系

基于 AQS 分析 Condition_加锁_02

在这图里面就能看出来,Node 和 ConditionObject是AQS中的内部类

ConditionObject类里面有什么重要的信息

// 条件队列的第一个节点
private transient Node firstWaiter;
// 条件队列的最后一个节点
private transient Node lastWaiter;

通过这两个属性就知道条件队列它有自己的等待队列,还有一些其他的方法等待分析到的时候再看,先看ReentrantLock加锁的方法,这个之前讲过了 ​​基于AQS 分析 ReentrantLock​​### 再看看condition.await()这个方法

public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 添加一个等待者到条件等待队列中
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

分析一下这个方法 addConditionWaiter()

private Node addConditionWaiter() {
// 定义个t指针,指向条件等待队列的尾部节点,此时为空
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建一个Node节点
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}

创建一个Node.CONDTION 节点

Node node = new Node(Thread.currentThread(), Node.CONDITION);
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}

基于 AQS 分析 Condition_加锁_03

firstWaiter 指针指向了这个新的node节点,此时t指针也指向了新的node节点,lastWaiter也指向了该node节点,返回了这个node节点

基于 AQS 分析 Condition_等待队列_04

该走下面的fullyRelease(node)方法

final int fullyRelease(Node node) {
boolean failed = true;
try {
// 因为前面获取锁来着,所以此时state应该为1
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}

因为前面获取锁来着,所以此时state应该为1,走到这个方法中 release(savedState)

public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

走这个方法尝试着释放锁 tryRelease(arg) arg =1

protected final boolean tryRelease(int releases) { // releases = 1
// 1 - 1 = 0
int c = getState() - releases; // 0
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
// 设置当前锁的独占线程为null
setExclusiveOwnerThread(null);
}
// 重新设置state等于0
setState(c);
return free;
}

此时state 等于0,exclusiveOwnerThread = null ,所以释放锁成功了,因为这里面就一个线程加锁了,所以AQS中的等待队列的头节点是空值 head==null,因此此时h也是空值,所以直接返回true

int savedState = fullyRelease(node);

锁释放成功后,此时返回的savedState = 1,进入到下面的方法中

while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}

看一下这个关键的地方

final boolean isOnSyncQueue(Node node) {
// 这个新创建的node的waitStatus 是-1 所以 node.waitStatus == Node.CONDITION 成立,返回了false
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}

此时这里返回了false,那么!isOnSyncQueue(node)此时就是true所以这里通过 LockSupport.park(this)将线程挂起

再看看signal方法

同理前面也得获取锁通过ReentrantLock加锁的方式,此时state 就是1了独占线程就是当前线程,先看看signal方法

condition.signal();

public final void signal() {
// 如果不是当前线程加的锁那么就抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 获取等待队列的第一个等待者
Node first = firstWaiter;
// 此时不为空,所以走 doSignal(first)
if (first != null)
doSignal(first);
}

此时不为空,所以走 doSignal(first),看看这个方法里面有什么

private void doSignal(Node first) {
do {
// 将firstWaiter 重新赋值到当前等待者的下一个等待着,不过此时等待者就一个,所以first.nextWaiter = null,lastWaiter = null
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
// first.nextWaiter 置于 null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}

基于 AQS 分析 Condition_Java_05

由图可知 first.nextWaiter 是 null,走到这个方法里面 !transferForSignal(first)

final boolean transferForSignal(Node node) {
// 此时将CONDITION设置为0,成功则返回true
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;

Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}

ConditionObject中的等待队列

基于 AQS 分析 Condition_等待队列_06

接着走enq(node)方法,注意此时是将ConditionObject中的等待队列中的节点,放入到AQS中的等待队列中,好方便下一次获取到锁

private Node enq(final Node node) {
for (;;) {
// 此时tail是空值,所以t也是空值,那么初始化队列
Node t = tail;
if (t == null) { // Must initialize
// 创建了一个空的头节点
if (compareAndSetHead(new Node()))
// 将尾部节点tail指针也指向了当前创建的头节点
tail = head;
} else
// 此时t不是空值了因为上一次已经创建出来节点了【头节点】,此时就是将当前的节点的前驱指针指向头节点
node.prev = t;
// 在cas操作设置tail的尾部指针
if (compareAndSetTail(t, node)) {
// 头节点的next指针指向新加入的节点
t.next = node;
return t;
}
}
}
}

此时的AQS中的条件队列

基于 AQS 分析 Condition_加锁_07

enq(final Node node)返回的是头节点,因为头节点的waitStatus是0,所以ws > 0 不成立,因此走到了这个方法!compareAndSetWaitStatus(p, ws, Node.SIGNAL) 将头节点的waitStatus设置成了-1。此时就将之前在ConditionObject等待队列中的节点加入到了AQS中的条件队列中了,此时就可以当现在持有锁的线程释放锁后去唤醒在AQS中等待的队列去争抢锁了。

持有锁的线程释放锁之后去唤醒AQS中等待队列

先看看持有锁的线程是怎么释放锁的

public void unlock() {
sync.release(1);
}

通过 sync.release(1); 方法此时传进去的值是1

public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

再尝试释放锁tryRelease(arg)

protected final boolean tryRelease(int releases) {
// state -1 此时就是 0了
int c = getState() - releases;
// 如果不是当前线程那么就抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 此时c 等于0
if (c == 0) {
free = true;
// 将独占线程置为null
setExclusiveOwnerThread(null);
}
// 重新设置state =0
setState(c);
// 返回true
return free;
}

定义一个h指针指向了头节点,此时头节点不为空,并且头节点的waitStatus = -1,所以if (h != null && h.waitStatus != 0) 条件成立,走unparkSuccessor(h)方法,并且返回true

基于 AQS 分析 Condition_等待队列_08

private void unparkSuccessor(Node node) { // 头节点
// 此时头节点的waitStatus的值为-1
int ws = node.waitStatus;
if (ws < 0)
// 将头节点的waitStatus 设置为0
compareAndSetWaitStatus(node, ws, 0);
// 定义一个s指针指向头节点的下一个节点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}

基于 AQS 分析 Condition_后端_09

因为s!=null,并且s的waitStatus = 0 所以通过LockSupport.unpark(将线程唤醒),此时唤醒之后接着当时挂起地方继续往下执行,想想在哪里挂起的

基于 AQS 分析 Condition_Java_10

再看看!isOnSyncQueue(node) 这个方法

final boolean isOnSyncQueue(Node node) {
// 此时node的waitStatus是0不是Node.CONDITION ,并且 node.prev也不是null
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 此时的node.next是null
if (node.next != null) // If has successor, it must be on queue
return true;
// 所以走到了这里
return findNodeFromTail(node);
}

我们再看看这个findNodeFromTail(node)方法

private boolean findNodeFromTail(Node node) {
// 定义一个t指针指向了AQS队列中的尾部节点
Node t = tail;
for (;;) {
// t 是 node所以返回了true
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}

基于 AQS 分析 Condition_内部类_11

那么此时就退出了while循环了,走下面的逻辑代码了

if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;

又回到了我们熟悉的获取锁的流程了acquireQueued(node, savedState)

final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取当前节点的前驱节点那就是头节点呗目前看来
final Node p = node.predecessor();
// 此时p ==head,再一次获取锁,此时获取成功了
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}

看看这个tryAcquire(arg)方法

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 此时state 是0,那么c就是0
int c = getState();
if (c == 0) {
// 设置state为1 ,设置独占线程并且返回true
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

此时就该走下面的修改指针的逻辑了

setHead(node);
p.next = null; // help GC
failed = false;

看看setHead里面是什么东西

private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}

基于 AQS 分析 Condition_内部类_12

到这里其实就是这个线程已经获取到锁了,就可以继续往下执行逻辑了