推迟 thenApplyAsync 执行

defer thenApplyAsync execution

我有以下场景。

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
result.thenRun(() -> {
        ...
    });
// ....
// after some more code, based on some condition I attach the thenApply() to result.

if ( x == 1) {
    result.thenApplyAsync(t -> {

        return null;
    });
}

问题是,如果 CompletableFuture 线程在主线程到达 thenApplyAsync 之前完成执行怎么办? CompletableFuture 结果是否应附加到 thenApply。即是否应在定义 CompletableFuture.supplyAsync() 本身时声明回调?

还有执行顺序是什么? thenRun() 总是最后执行(在 thenApply() 之后) ?

使用这个策略有什么缺点吗?

你似乎漏掉了一个重点。当您链接一个依赖函数时,您不会改变您调用链接方法的未来。

相反,这些方法中的每一个 returns 一个 new 完成阶段代表相关操作。

由于您将两个相关操作附加到 result,它们表示传递给 supplyAsynctask,因此这两个操作之间没有任何关系。它们可能 运行 以任意顺序甚至在不同线程中同时出现。

由于您没有将 thenApplyAsync 返回的未来存储在任何地方,因此其评估结果无论如何都会丢失。假设您的函数 returns 是与 T 相同类型的结果,您可以使用

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}

用新的未来替换可能完成的未来,新的未来只有在评估指定函数的结果时才会完成。通过 thenRun 在原始 future 注册的 运行nable 仍然不依赖于这个新的 future。注意thenApplyAsync不带executor会一直使用默认的executor,不管其他future是用哪个executor完成的

如果你想确保Runnable在任何其他阶段之前已经成功执行,你可以使用

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
CompletableFuture<Void> thenRun = result.thenRun(() -> {
    //...
});
result = result.thenCombine(thenRun, (t,v) -> t);

另一种选择是

result = result.whenComplete((value, throwable) -> {
    //...
});

但在这里,即使在异常情况下(包括取消),代码也将始终执行。如果您只想在成功的情况下执行代码,则必须检查 throwable 是否为 null


如果你想确保 运行nable 运行s 在两个动作之后,最简单的策略是将它链接在 if 语句之后,当最终完成阶段定义:

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}
result.thenRun(() -> {
    //...
});

如果这不是一个选项,您将需要一个不完整的未来,您可以根据任一结果完成它:

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);

//...

CompletableFuture<T> finalStage = new CompletableFuture<>();
finalStage.thenRun(() -> {
    //...
});

// ...

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}
result.whenComplete((v,t) -> {
    if(t != null) finalStage.completeExceptionally(t); else finalStage.complete(v);
});

finalStage 最初没有定义完成方式,但我们仍然可以链接相关操作。一旦我们知道实际的未来,我们就可以链接一个处理程序,它将完成我们的 finalStage 和我们得到的任何结果。


最后一点,没有 …Async 的方法,如 thenRun,对评估线程的控制最少。它们可能会在未来完成的任何线程中执行,例如您示例中 executor 的线程之一,但也可能直接在调用 thenRun 的线程中执行,甚至更不直观,在您的原始示例中, 运行nable 可能会在不相关的 thenApplyAsync 调用期间执行。