为什么这段代码的结果是非确定性的?

huangapple go评论62阅读模式
英文:

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&lt;String&gt; outer = CompletableFuture.supplyAsync(() -&gt; &quot;outer&quot;);
        CompletableFuture&lt;String&gt; inner = CompletableFuture.supplyAsync(() -&gt; &quot;inner&quot;);
        inner.completeExceptionally(new IllegalArgumentException());
        CompletableFuture&lt;String&gt; both = outer.thenApply(s -&gt; {
            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(&quot;Errored&quot;);
        }

答案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&lt;String&gt; inner = CompletableFuture.supplyAsync(() -&gt; &quot;inner&quot;);

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

理解正在发生的情况的关键在于completeExceptionallyjavadoc

> 如果尚未完成,则导致对get()和相关方法的调用抛出给定的异常。

在您的示例中,您正在创建两个将在异步完成的 futures,然后您调用completeExceptionally来告诉其中一个 futures 抛出异常,而不是传递结果。

根据您的说法,似乎有时在调用completeExceptionallyinner 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.

huangapple
  • 本文由 发表于 2020年10月20日 22:54:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/64447768.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定