我有以下场景。
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()
之后)?
使用此策略有什么缺点吗?
最佳答案
您似乎忽略了重要的一点。当您链接依赖函数时,您不会改变调用链接方法的 future 。
相反,这些方法中的每一个都会返回一个代表相关操作的新完成阶段。
由于您将两个依赖操作附加到 result
,它们代表传递给 supplyAsync
的 task
,因此这两个操作之间没有关系。它们可以以任意顺序运行,甚至可以同时在不同的线程中运行。
由于您没有将 thenApplyAsync
返回的 future 存储在任何地方,因此其评估结果无论如何都会丢失。假设您的函数返回与 T
类型相同的结果,您可以使用
if(x == 1) {
result = result.thenApplyAsync(t -> {
return null;
});
}
将可能完成的 future 替换为新的 future ,该新的 future 仅在评估指定函数的结果时才完成。通过 thenRun
在原始 future 注册的可运行对象仍然不依赖于这个新的 future。请注意,没有执行器的 thenApplyAsync 将始终使用默认执行器,无论使用哪个执行器来完成另一个 future。
如果您想确保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
。
如果您想确保可运行对象在两个操作之后运行,最简单的策略是在定义最终完成阶段时将其链接到 if
语句之后:
if(x == 1) {
result = result.thenApplyAsync(t -> {
return null;
});
}
result.thenRun(() -> {
//...
});
如果这不是一个选择,您将需要一个不完整的 future ,您可以根据任一结果完成:
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
最初没有定义的完成方式,但我们仍然可以链接相关操作。一旦我们知道了实际的 future ,我们就可以链接一个处理程序,它将以我们得到的任何结果完成我们的 finalStage
。
最后一点,没有 ...Async
的方法(如 thenRun
)对评估线程提供的控制最少。它们可能会在未来完成的任何线程中执行,例如示例中的 executor 线程之一,但也可以直接在调用 thenRun 的线程中执行,甚至不太直观,在您的原始示例中,可运行对象可能会在不相关的 thenApplyAsync 调用期间执行。
关于java - 推迟 thenApplyAsync 执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50735686/