public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
// 指向条件队列的第一个node节点
private transient Node firstWaiter;
// 指向条件队列的最后一个node节点
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
}
await方法:
public final void await() throws InterruptedException {
// 判断当前线程是否是中断
if (Thread.interrupted())
throw new InterruptedException();
// 将调用await方法的线程包装成node并加入条件队列中,并返回当前node
Node node = addConditionWaiter();
// 完全释放当前线程对应的锁,
// 为什么要释放锁那?加着锁挂起后,谁还能救你那
int savedState = fullyRelease(node);
// 0 在condition队列挂起期间未接收中断信号
// -1 在condition队列期间接收到中断信号
// 1 在condition队列挂起期间未接收到中断信号,但是在迁移到“阻塞队列”过程中,接收过中断信号
int interruptMode = 0;
// isOnSyncQueue返回true表示当前线程对应的node已经迁移到“阻塞队列”
// false 说明当前node 仍然还在条件队列中,需要继续park
while (!isOnSyncQueue(node)) {
// 挂起当前node对应的线程,接下去去看看signal过程
LockSupport.park(this);
// 什么时候会被唤醒?都有哪几种情况
// 1、常规路径:外部线程获取到锁之后,调用signal()方法 转移条件队列的头节点到阻塞队列,当
// 这个节点获取到锁后,会唤醒
// 2.转移至阻塞至阻塞队列后,发现阻塞队列中的前驱节点状态是 取消状态,此时会唤醒当前节点
// 3.当前节点挂起期间时,被外部线程使用中断唤醒
// checkInterruptWhileWaiting: 就算在condition队列挂起期间,线程发生中断了,
// 对应的node也会被迁移到“阻塞队列”
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// acquiredQueued:竞争队列的逻辑
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 考虑下 node.nextWaiter != null 条件什么时候成立那
// 其实是node在条件队列内时,如果被外部线程中断唤醒时,会加入到阻塞队列,
// 但是并未设置nextWaiter != null
if (node.nextWaiter != null) // clean up if cancelled
// 清理条件队列取消状态的节点
unlinkCancelledWaiters();
// 条件成立:说明挂起期间 发生过中断(1.条件队列内的挂起 2.条件队列之外的挂起)
if (interruptMode != 0)
// 条件队列内发生中断,此外await方法抛出中断异常
// 条件队列外发生的中断,交给你的业务处理,如果你不处理,那什么事也不会发生
reportInterruptAfterWait(interruptMode);
}
private int checkInterruptWhileWaiting(Node node) {
// thread.interrupted 返回当前线程中断标记位,并且重置当前标记为false
return Thread.interrupted() ?
// transferAfterCancel 这个方法只有在线程是被中断唤醒时才会调用
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
// 条件成立:说明当前node一定是在条件队列内,因为signal迁移节点
// 到阻塞队列时,会将节点的状态修改为0
if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
// 中断唤醒的node也会被加入到阻塞队列中
enq(node);
// true:表示是在条件队列内被中断的
return true;
}
// 执行到这里有几种情况?
// 1、当前node已经被外部线程调用 signal方法将其迁移到阻塞队列内
// 2、当前node正在被外部线程调用 signal方法将其迁移到阻塞队列中
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
signal方法
public final void signal() {
// 判断调用signal方法的线程是否是独占锁持有线程,如果不是,直接抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
// firstWaiter = first.nextWaiter 因为当前first马上要出条件队列
// 如果当前节点的下一个节点是NULL,说明条件队列只有当前一个节点了。。
// 当前出队后,整个队列就空了
// 所以需要更新lastWaiter = null
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
// 当前first节点 出条件队列,断开和下一个节点的关系
first.nextWaiter = null;
// transferForSignal(first)
// true:当前first节点迁移到阻塞队列成功 false迁移失败
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// cas 修改当前节点的状态,修改为0,因为当前节点马上要迁移到 阻塞队列中
// 成功:当前节点在条件队列中状态正常
// 失败: 1、取消状态(线程await时,未持有锁,最终线程对应的node会设置为取消状态)
// 2、node对应的线程挂起期间,被其他线程使用中断信号
唤醒过(就会主动进入到阻塞队列中),这时候也会修改状态为0
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
// 条件一:ws >0 成立:说明前驱节点状态在阻塞队列中是取消状态,唤醒当前节点
// 条件二: 前置条件(ws <= 0)
// compareAndSetWaitStatus(p,ws,Node.SIGNAL)表示设置前驱节点状态 SIGANAL状态成功
// compareAndSetWaitStatus(p,ws,NOde.SIGNAL)返回false ==> 什么时候返回false
// 当前驱node对应的线程是lockInterrupt 入队的node时,是会响应中断的,外部线程给前驱线程中断
// 信号之后,前驱node状态修改为取消状态,并且执行出队逻辑
// 前驱节点状态只要不是0 或者-1,那么就唤醒当前线程
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}