FutureTask源码 - get方法解析

2,241 阅读2分钟

「这是我参与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
    }