概述
Condition虽然在工作中没有机会用上, 所以也没太关注,最近开始看JDK源码的时候发现里面用了大量的condition,所以来研究一下内部实现原理.
Condition是AQS的一个内部类,是一个有头尾指针的单项链表 通过维护一个 等待队列 和 AQS的 同步队列 之间的交互达到等待唤醒的机制。
整体的图其实就是这么简单
public static void main(String[] args) throws Exception{
# 创建了一个同步队列
ReentrantLock lock = new ReentrantLock();
# 创建了一个等待队列
Condition condition = lock.newCondition();
new Thread(()->{
try {
lock.lock();
System.out.println("开始阻塞");
# 调用了await方法后就会把当前线程放入等待队列,并且唤醒同步队列中的Node
condition.await();
# 能执行下来说明自己已经被踢出等待队列,并且放入同步队列,并且又抢到了锁
System.out.println("阻塞结束");
}catch (Exception e){
}
finally {
lock.unlock();
}
}).start();
Thread.sleep(1000);
new Thread(()->{
try {
lock.lock();
System.out.println("拿到锁释放资源");
# 找到等待队列中的头节点踢出并插入到同步队列中去,同步队列的Node需要等当前线程释放锁后争抢
condition.signal();
Thread.sleep(1000);
System.out.println("释放结束");
}catch (Exception e){
} finally {
lock.unlock();
}
}).start();
System.in.read();
}
等待队列
在AQS的内部类 ConditionObject 就是一个等待队列,而且一个 ReentrantLock 可以创建多个等待队列。先来看看 ConditionObject源码,可以看到就是一个头尾指针的队列,没啥东西
public class ConditionObject implements Condition, java.io.Serializable {
# 等待队列的头指针
private transient Node firstWaiter;
# 尾指针
private transient Node lastWaiter;
public ConditionObject() { }
}
await
await方法是把当前线程封装成一个 Node节点然后放入等待队列,我们看看实现逻辑
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
# 创建一个节点并且插入了队列尾部
Node node = addConditionWaiter();
# 释放 AQS的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
# 判断是不是在 AQS的同步队列中,如果是就退出循环
while (!isOnSyncQueue(node)) {
# 当前线程陷入睡眠,等待唤醒
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
# 唤醒之后自己已经在同步队列中了,走AQS获取锁的逻辑
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
# 创建一个等待队列的节点并插入队列尾部
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
signal
唤醒逻辑就是把等待队列的Node丢到同步队列中,很简单主要关注 transferForSignal 方法
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
# enq 就是加入 AQS的同步队列
Node p = enq(node);
int ws = p.waitStatus;
# 下面的是优化速度逻辑,不用关注也可以
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
总结
至此,Condition大体逻辑就梳理清楚了, 调用 await方法释放锁,然后自己加入 等待队列, signal 方法让Node放入 同步队列 去争抢锁资源。