「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」
💡 get方法get方法是获取结果,如果当前任务仍然在执行中,那么将阻塞到获取结果
当状态为≤COMPLETING,说明线程当前正在运行中没有被取消,那么执行awaitDone方法阻塞等待.
report方法是收尾工作,返回结果.
public V get() throws InterruptedException, ExecutionException {
int s = state;
//执行状态是COMPLETING时执行awaitDone将线程加入等待队列中并挂起线程
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
💡 awaitDone方法
该方法是实现阻塞的关键方法
1.查看入参是否设置了超时时间timed = true.来是否设置超时时间
2.自旋for(;;)
1.先判断当前线程是否中断,抛出异常,移除在等待队列中的等待节点
2.判断当前状态,如果大于COMPLETING,说明任务已经结束.线程置空并返回结果
3.如果还在执行state==COMPLETING,说明*当前任务已经执行结束,但是任务执行线程还没来得及给outcome赋值.*挂起线程,让其他任务执行线程优先执行.
4.等待节点为空,说明任务尚未执行,那么初始化一个等待节点.
5.如果没有入队列!queued,那么放到队列的头结点(由于是else if,因此创建等待节点时不会入队列)
5.如果设置了超时时间,计算当前还有多少时间超时t,如果超时t<0,删除对应节点并返回当前状态.阻塞t时间
6.阻塞等待直到被其他线程唤醒.(当任务线程执行结束,就会唤醒等待线程finishCompletion
)
private int awaitDone(Boolean timed, long nanos)
throws InterruptedException {
//如果设置了超时时间timed=true,那么deadline就是超时时间,超过就超时了
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 用作线程的等待节点
WaitNode q = null;
Boolean queued = false;
// 自旋
for (;;) {
//如果当前线程被中断,删除等待队列中的节点,并抛出异常
if (Thread.interrupted()) {
// 移除等待队列中的等待节点
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//如果执行状态已经完成或者发生异常,直接返回结果
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//如果执行状态是正在执行,说明任务已经完成.那么现在需要给其他正在执行的任务让路,挂起线程.
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
//第一次进入循环,创建等待节点
else if (q == null)
q = new WaitNode();
//将节点加入到等待队列中,waiters相当于头阶段,不断将头结点更新为新节点
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
//如果设置了超时时间,在进行下次循环前查看是否已经超时,如果超时删除该节点进行返回
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//挂起当前节点,阻塞
LockSupport.parkNanos(this, nanos);
} else
LockSupport.park(this);
}
}
触发流程:
1.第一轮for循环,执行逻辑q == null,新建等待节点q,循环结束
2.第二轮for循环,执行!q,入队,循环结束.
3.第三轮for循环,进行阻塞等待或者阻塞特定时间,直到阻塞被其他线程唤醒.
4.唤醒后第四轮for循环,根据前三个条件进入对应的逻辑中
💡 finishCompletion该方法主要用于唤醒线程.当任务结束或者异常时,会调用该方法
被唤醒的线程就会从awaitDown方法中的LockSupport的park或者parkNanos方法处唤醒,然后继续执行awaitDown方法
private void finishCompletion() {
// 遍历等待节点
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
// 唤醒等待线程
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}