我知道这个问题很老,但我想用源代码来解释这个问题。
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
private CompletableFuture<Void> uniAcceptStage(Executor e,
Consumer<? super T> f) {
if (f == null) throw new NullPointerException();
Object r;
if ((r = result) != null)
return uniAcceptNow(r, e, f);
CompletableFuture<Void> d = newIncompleteFuture();
unipush(new UniAccept<T>(e, d, this, f));
return d;
}
这是来自 java 16 的源代码,我们可以看到,如果我们触发 thenAccept,我们会将一个空的执行器服务引用传递给我们的函数。从第二个函数 uniAcceptStage() 第二个 if 条件。如果结果不为空,它将触发 uniAcceptNow()
if (e != null) {
e.execute(new UniAccept<T>(null, d, this, f));
} else {
@SuppressWarnings("unchecked") T t = (T) r;
f.accept(t);
d.result = NIL;
}
如果执行器服务为空,我们将使用 lambda 函数 f.accept(t) 来执行它。如果我们从主线程触发 thenApply/thenAccept,它将使用主线程作为执行线程。
但是,如果我们无法从上一个可完成的未来中获得先前的结果,我们将使用 uniPush 函数将当前的 UniAccept/Apply 推入堆栈。UniAccept 类有 tryFire() 将由我们的 postComplete() 函数触发
final void postComplete() {
/*
* On each step, variable f holds current dependents to pop
* and run. It is extended along only one path at a time,
* pushing others to avoid unbounded recursion.
*/
CompletableFuture<?> f = this; Completion h;
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture<?> d; Completion t;
if (STACK.compareAndSet(f, h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
NEXT.compareAndSet(h, t, null); // try to detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}