问题描述
我希望以下代码始终打印“错误”,但有时却打印“外部”。我要发生的是使“外部”完成,使“内部”使用“外部”的结果生成自己的结果,如果任何将来失败,都会失败。我在做什么错了?
CompletableFuture<String> outer = CompletableFuture.supplyAsync(() -> "outer");
CompletableFuture<String> inner = CompletableFuture.supplyAsync(() -> "inner");
inner.completeExceptionally(new IllegalArgumentException());
CompletableFuture<String> both = outer.thenApply(s -> {
try {
String i = inner.get();
return s + i;
} catch (InterruptedException |ExecutionException e) {
throw new IllegalStateException(e);
}
});
try {
String o = both.get();
System.out.println(o);
} catch (ExecutionException | InterruptedException e) {
System.err.println("Errored");
}
解决方法
了解这一点实际上很简单。您需要对此进行更仔细的研究:
CompletableFuture<String> inner = CompletableFuture.supplyAsync(() -> "inner");
具体来说,您说的是supplyAsync
,这意味着在另一个线程中。在主线程(或其他任何线程)中,您可以执行inner.completeExceptionally(new IllegalArgumentException());
。
哪个线程应该赢?来自supplyAsync
的一个还是您运行inner.completeExceptionally
的一个?当然,completeExceptionally
的文档中提到了这一点……这是一个基本的“竞赛”,首先到达inner
的那个线程到达“完成”(正常或通过异常)。
了解发生了什么的关键是在completeExceptionally
的{{3}}中。
如果尚未完成,则导致
get()
和相关方法的调用引发给定异常。
在您的示例中,您正在创建两个期货,这些期货将异步完成,然后您调用completeExceptionally
来告诉其中一个期货引发异常而不是传递结果。
根据您的发言,看来inner
的将来有时在您致电completeExceptionally
时已经完成了。在这种情况下,completeExceptionally
调用将不会有任何影响(按照规范),随后的inner.get()
将传递(已经)计算出的结果。
这是比赛条件。