FutureTask源码 - get方法解析

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

💡 get方法

get方法是获取结果,如果当前任务仍然在执行中,那么将阻塞到获取结果

当状态为≤COMPLETING,说明线程当前正在运行中没有被取消,那么执行awaitDone方法阻塞等待.

report方法是收尾工作,返回结果.

1
2
3
4
5
6
7
ini复制代码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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
ini复制代码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方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ini复制代码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
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%