十七、condition之再看await方法

69 阅读4分钟

再看await方法

线程被唤醒后执行流程:

  • 唤醒后,要先确认是signal唤醒还是中断唤醒,还是signal唤醒后被中断
  • 确保当前线程的node已经在AQS链表中
  • 执行acquireQueued方法,等待获取锁资源
  • 在获取锁资源后,要确认是否在获取锁资源过程中被中断过;如果中断过,并且不是THROW_IE,那就确保interruptMode是REINTERRUPT
  • 确认当前node已经不在条件队列中了
  • 最终根据interruptMode来决定具体要做的事情
    • 0:什么也不用做
    • THROW_IE:抛出异常
    • REINTERRUPT:执行interrupt方法
public final void await() throws InterruptedException {
	// 如果线程中断,则抛出中断异常
	if (Thread.interrupted())
		throw new InterruptedException();
	// 将当前线程加入到条件队列中
	Node node = addConditionWaiter();
	// 释放所有锁资源,并且保留重入的次数
	int savedState = fullyRelease(node);
	// interruptMode表示唤醒模式
	int interruptMode = 0;
	// 如果线程不在同步队列中,则挂起。
	// 有这样一种情况,线程在执行完fullyRelease方法后,时间片用完了,另一个线程拿到锁资源
	// 并且另一个线程调用了signal方法,signal方法会将当前线程从条件队列中转移到同步队列中
	// 此时,如果当前线程获得cpu资源继续执行,是不需要执行LockSupport.park(this)方法的
	while (!isOnSyncQueue(node)) {
		LockSupport.park(this);
		// 走到这里,说明当前线程被唤醒
		// 第一种情况:被signal方法唤醒,节点一定被放到同步队列中
		// 第二种情况:被同步队列中前一个节点唤醒,节点一定被放到同步队列中
		// 第三种情况:被中断唤醒,节点还没有放到同步队列中
		// 第四种情况:被中断唤醒,节点已经放到同步队列中
		// 对于第三种情况,会在执行checkInterruptWhileWaiting方法时,将节点放到同步队列中
		if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
			break;
	}
	// 线程走到这里,节点必定已经放到同步队列中了
	// acquireQueued方法只有在线程获取到锁后才会返回
	// acquireQueued方法中线程被唤醒去竞争锁资源有两种情况
	// 一种是被同步队列前一个节点唤醒,另一种是被中断唤醒
	// 如果是被中断唤醒,可能后续有业务要处理这个中断
	// 但是acquireQueued方法中的parkAndCheckInterrupt方法
	// 将线程中断标志位设置成false了,这是不合理的
	// 所以,如果线程被中断唤醒,acquireQueued方法会返回true
	// 这样在reportInterruptAfterWait方法中会将中断标志位设置成true
	if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
		// 进入这里说明线程是先被移动到同步队列中,然后被中断
		interruptMode = REINTERRUPT;
	if (node.nextWaiter != null)
		// 当前线程被中断唤醒,当前node还在条件队列中,需要脱离条件队列
		unlinkCancelledWaiters();
	// 如果interrupteMode是0,说明线程在signal后以及持有锁过程中,没有被中断过,什么事也不用做
	if (interruptMode != 0)
		// interruptMode是THROW_IE:抛出异常
		// interruptMode是REINTERRUPT:执行线程interrupt方法
		// 需要注意的是,当await方法抛出中断异常时,线程是已经持有锁的
		// 所以最好在捕获异常后将锁资源释放掉
		reportInterruptAfterWait(interruptMode);
}
private int checkInterruptWhileWaiting(Node node) {
	// 如果线程没有被中断,返回0
	// 如果线程在移动到同步队列前发生中断,返回THROW_IE,值为-1
	// 如果线程在移动到同步队列后发生中断,返回REINTERRUPT,值为1
	return Thread.interrupted() ?
		(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
		0;
}
final boolean transferAfterCancelledWait(Node node) {
	if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
		// 进入这里说明线程在移动到同步队列前发生中断
		// 此时节点的状态还是CONDITION,可以通过CAS改成0
		// 将node添加到AQS节点,此时条件队列和同步队列中都有此节点了
		// 不过在await方法的unlinkCancelledWaiters方法中会将此节点从条件队列中删除
		enq(node);
		return true;
	}

	// 进入这里说明线程在移动到同步队列后发生中断
	// 判断同步队列中是否有此节点(其实没有必要)
	// 如果没有等待节点放入同步队列中
	while (!isOnSyncQueue(node))
		Thread.yield();
	return false;
}
// 删除掉condition链表中waitStatus值不为-2的节点
private void unlinkCancelledWaiters() {
	Node t = firstWaiter;
	Node trail = null;
	while (t != null) {
		Node next = t.nextWaiter;
		if (t.waitStatus != Node.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)
		// Thread.currentThread().interrupt();
		selfInterrupt();
}