1.前言
在日常开发中不可避免会出现异步任务,而我们又需要拿到异步任务的执行结果才能继续下一步逻辑。想要拿到异步任务结果,你一定会想到要使用Future
类,那么你是否知道Future
是如何及时知道异步任务执行完成的呢?在了解原理之前,先来看一下大体流程,然后再一步一步对其进行剖析。
2.异步任务
2.1 创建任务
public static class Task implements Callable<String> {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(3);
return "异步任务执行完成";
}
}
2.2 提交线程池执行
Task task = new Task();
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(task);
2.3 构建FutreTask
任务提交给线程池执行后,线程池会把我们的任务构建成FutreTask
并返回
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
2.4 获取异步结果
线程池将构建的FutreTask
返回给我们,由于FutreTask
实现了Future
接口,因此可以通过Future
来接收。想要获取结果,只需要调用get()
方法即可
String result = future.get();
2.5 get()
实现
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
get()
逻辑首先判断任务状态是否执行完成,如果没有执行完成就进行阻塞;如果执行完成直接返回任务结果
2.6 阻塞实现
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
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();
// 1. 创建一个节点,结点中包含当前线程对象
else if (q == null)
q = new WaitNode();
// 2. 节点如果不在链表中,就放入链表
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;
}
// 3.在给定时间内阻塞当前线程
LockSupport.parkNanos(this, nanos);
}
else
// 4.阻塞当前线程
LockSupport.park(this);
}
}
阻塞实现逻辑:异步任务未执行完成,根据当前线程创建节点放入链表,然后阻塞当前线程
2.7 异步任务执行完成
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
// 1.遍历链表中的节点,取出结点中的线程
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
// 2.唤醒线程,使得线程可以继续往下执行,获取异步任务执行结果
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
}
在获取异步任务结果环节,由于异步任务未执行完成,会生成等待节点链表。当异步任务执行完成后,需要遍历等待节点链表,从链表节点中获取阻塞线程对象,对其进行唤醒,使得线程可以继续往下执行,拿到异步任务执行结果