ConditionObject源码分析
平时用ReentrantLock的时候,会用lock.newCondition()来创建一个条件,跟踪进去
public Condition newCondition() {
return sync.newCondition();
}
//Sync中的newCondition方法
final ConditionObject newCondition() {
return new ConditionObject();
}
ConditionObject是AbstractQueuedSynchronizer类中的一个内部类,
其继承关系如下
看一下Condition接口定义的方法
PS:首先要明确ConditionObject是在线程获得锁后,可以进行await释放资源进入睡眠并等待唤醒,signal则是唤醒同一个ConditionObject里的队列中头结点线程
从await方法入手
public final void await() throws InterruptedException {
if (Thread.interrupted())//抛出中断异常
throw new InterruptedException();
Node node = addConditionWaiter();//添加一个节点到condition队列
int savedState = fullyRelease(node);//释放资源
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//保证节点已进入sync队列
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//进入sync队列后开始请求资源
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) //删除condition队列中已取消的节点
unlinkCancelledWaiters();
if (interruptMode != 0)//中断模式处理
reportInterruptAfterWait(interruptMode);
}
上面方法的逻辑是:
● await方法是能响应中断的,首先对线程是否中断进行判断
● 然后添加一个节点到condition队列
● 接着开始释放资源并返回资源数
● 再接着,如果节点不在在sync队列,休眠线程,线程被唤醒的时候进行中断检测,然后继续检查还在不在sync队列
● acquireQueued方法是开始请求获取锁,在上一篇文章已分析过,这个方法会返回线程在获得锁后是否中断,或者抛出异常。
● 当上面返回true,对interruptMode进行判断,不是THROW_IE(抛出异常)就设置成REINTERRUPT(重新设置中断标志)
● 如果condition队列中还有其他节点(node.nextWaiter != null),把节点状态不是CONDITION的从condition队列中移除
● 最后对中断模式进行不同处理
根据上面的逻辑一个个方法跟踪下去
先看插入一个节点到condition队列
private Node addConditionWaiter() {
Node t = lastWaiter;//队列尾节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();//清理节点
t = lastWaiter;//最终的尾节点
}
//生成新的等待节点,状态是CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)//队列为空
firstWaiter = node;
else
t.nextWaiter = node;//插到队尾
lastWaiter = node;//刷新队尾节点
return node;//返回新增的节点
}
开始释放资源
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();//获取资源数
if (release(savedState)) {
//该方法前篇文章分析过,释放资源并唤醒一个线程
failed = false;
return savedState;//返回资源数
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)//抛异常了
node.waitStatus = Node.CANCELLED;
}
}
检查是否在sync队列,上面传进的节点状态为CONDITION,所以直接返回false了,进入睡眠了等待唤醒
final boolean isOnSyncQueue(Node node) {
//状态为CONDITION或node.prev为null直接返回false,因为处于sync队列中为获得锁的线程肯定有前驱节点
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null)//有后置节点那一定在sync队列
return true;
//如果node.prev不为空还不能确定就在sync队列,应为有可能CAS插入队列失败,所以还要遍历sync队列
return findNodeFromTail(node);
}
接着看线程被唤醒后,进行中断检测
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//CAS设置节点状态从CONDITION变为0,说明是condition队列的节点,所以返回true要抛中断异常
enq(node);//插入sync队列
return true;
}
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
可以看出线程如果等待过程中被设置了中断标志(注意:这里没有清除中断标志),就要看transferAfterCancelledWait方法的返回值
看condition队列调整的函数
private void unlinkCancelledWaiters() {
Node t = firstWaiter;//从头结点开始
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
//把不是CONDITION状态的移出队列
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)//说明前任要被移出队列,刷新尾节点
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
中断处理比较简单,要么抛异常,要么重设中断标志,要么啥都不干
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
await方法大致分析完,现在看signal方法
public final void signal() {
//不是当前线程获得锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;//取出condition队列的头结点进行唤醒
if (first != null)
doSignal(first);
}
//目的是唤醒队列的不为空的头结点
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
//到这里就把头结点移出了
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//无法CAS成功,说明该节点不是CONDITION状态了(被取消了)
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//到这里说明节点状态是等待执行状态了
//enq是插入sync队列,会返回sync队列前任尾节点(具体看前篇文章)
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
//上面之所以唤醒node线程,是因为无法把它的前驱节点设置状态为SIGNAL
return true;
}
到这就基本分析完了,总结一下流程:
假设有ConditionObject为con,当前获得锁的线程 A 调用con的await方法,那当前线程就释放锁(全部资源),进入休眠,进入condition队列;当其他线程调用con的signal方法的时候,若此时的 A 已经是condition队列的头结点,那它就会被唤醒,进入sync队列并尝试获取和之前释放的同等数量的资源