关于CompletableFuture的一些个人理解
从CompletableFuture.supplyAsync 和 CompletableFuture.get入手
下面是supplyAsync的方法入口
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
Supplier<U> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<U> d = new CompletableFuture<U>();
e.execute(new AsyncSupply<U>(d, f));
return d;
}
可以看到,supplyAsync执行后是新建一个CompletableFuture对象,然后提交到指定的线程池中运行
现在看下实现Runnable方法的AsyncSupply类
public void run() {
CompletableFuture<T> d; Supplier<T> f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
d.completeValue(f.get());
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
run方法里有个主要的方法postComplete()
在子线程执行完成任务后,会通过postComplete()
方法会调用tryFire()
方法通知(唤醒: LockSupport.unpark(w)
)主线程
看下主线程在提交任务到线程池后在做啥,CompletableFuture.get()
public T get() throws InterruptedException, ExecutionException {
Object r;
return reportGet((r = result) == null ? waitingGet(true) : r);
}
get方法进来后如果子任务比较耗时还没结束 会进入waitingGet()
方法
waitingGet方法里有个最重要的方法就是阻塞主线程,也就是ForkJoinPool.managedBlock(q)
如果是自定义的线程池就会走else,如果用的默认的jvm给的唯一线程池会走if,这里我们看else,因为如果要阻塞,都会走到blocker.block()
blocker是static final class Signaller extends Completion implements ForkJoinPool.ManagedBlocker
public boolean block() {
if (isReleasable())
return true;
else if (deadline == 0L)
LockSupport.park(this);
else if (nanos > 0L)
LockSupport.parkNanos(this, nanos);
return isReleasable();
}
可以看到主线程的阻塞' LockSupport.park(this)'
简单总结,CompletableFuture.supplyAsync(Supplier<U> supplier,Executor executor)
将任务提交给线程池进行异步处理,
直到该任务处理结束才会处理结果,结果值则是放在CompletableFuture的属性字段'volatile Object result;'的,并且会尝试唤醒
主线程(如果调用了CompletableFuture.get方法的话);
而CompletableFuture.get()
在主线程中则是第一次会去看下是否已经有任务的处理结果了,如果没有,则会阻塞自身,等待子线程将其唤醒