英文:
Why is the result of this code non-deterministic?
问题
以下是翻译好的内容:
我预期以下代码始终会打印“错误”,但有时会打印“outerinner”。我希望的情况是让“outer”完成,并且让“inner”使用“outer”的结果生成自己的结果,如果任何一个 future 失败则整体失败。我做错了什么?
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");
}
英文:
I would expect the following code to always print "Errored", but sometimes it prints "outerinner". What I want to happen is for "outer" to complete and for "inner" to use "outer"'s result to generate its own result, failing if either future fails. What am I doing wrong?
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");
}
答案1
得分: 3
这实际上相当容易理解。您需要更仔细地看这一点:
CompletableFuture<String> inner = CompletableFuture.supplyAsync(() -> "inner");
特别要注意的是您在使用 supplyAsync
,这意味着在“不同的线程”中进行操作。而在您的主线程(或其他任何线程)中,您执行了:inner.completeExceptionally(new IllegalArgumentException());
。
哪个线程应该获胜?是来自 supplyAsync
的线程,还是您运行 inner.completeExceptionally
的线程?当然,completeExceptionally
的文档提到了这一点... 这是一个基本的“竞争”,哪个线程先到达以“完成”(正常完成或通过异常)inner
。
英文:
It's actually fairly trivial to understand this one. You need to look more closely at this:
CompletableFuture<String> inner = CompletableFuture.supplyAsync(() -> "inner");
specifically the fact that you are saying supplyAsync
, which means in a different thread. While in your main thread (or any other) you do : inner.completeExceptionally(new IllegalArgumentException());
.
Which thread is supposed to win? The one from supplyAsync
or the one where you run inner.completeExceptionally
? Of course, the documentation of completeExceptionally
mentions this... It's a basic "race", which thread reaches to "complete" (either normally or via an Exception) that inner
first.
答案2
得分: 1
理解正在发生的情况的关键在于completeExceptionally
的javadoc。
> 如果尚未完成,则导致对get()
和相关方法的调用抛出给定的异常。
在您的示例中,您正在创建两个将在异步完成的 futures,然后您调用completeExceptionally
来告诉其中一个 futures 抛出异常,而不是传递结果。
根据您的说法,似乎有时在调用completeExceptionally
时inner
future 已经完成。在这种情况下,completeExceptionally
调用将没有影响(根据规范),随后的inner.get()
将传递(已经)计算出的结果。
这是一个竞态条件。
英文:
The key to understanding what is going on is in the javadoc for completeExceptionally
.
> If not already completed, causes invocations of get()
and related methods to throw the given exception.
In your example, you are creating two futures which will be completed asynchronously, then you are calling completeExceptionally
to tell one of the futures to throw an exception rather than delivering a result.
Based on what you are saying, it appears that sometimes the inner
future has already completed by the time you call completeExceptionally
. In that case, the completeExceptionally
call will have no affect (as per the spec), and the subsequent inner.get()
will deliver the result that was (already) computed.
It is a race condition.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论