为什么Java8 CompletableFuture然后Compose会根据完成顺序生成不同的异常?

为什么Java8 CompletableFuture然后Compose会根据完成顺序生成不同的异常?,java,java-8,future,Java,Java 8,Future,我遇到了Java8 CompletableFuture-thenCompose方法的奇怪行为。我有两个测试,它们只在执行顺序上有所不同。这两个测试都模拟了在可完成的未来中生成的故障 @Test public void completedAfter() { CompletableFuture<String> future1 = new CompletableFuture<>(); CompletableFuture<String> future2

我遇到了Java8 CompletableFuture-thenCompose方法的奇怪行为。我有两个测试,它们只在执行顺序上有所不同。这两个测试都模拟了在可完成的未来中生成的故障

@Test
public void completedAfter() {
    CompletableFuture<String> future1 = new CompletableFuture<>();
    CompletableFuture<String> future2 = new CompletableFuture<>();

    future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("After: " + e));

    future1.complete("value");
    future2.completeExceptionally(new RuntimeException());
}

@Test
public void completedBefore() {
    CompletableFuture<String> future1 = new CompletableFuture<>();
    CompletableFuture<String> future2 = new CompletableFuture<>();

    future1.complete("value");
    future2.completeExceptionally(new RuntimeException());

    future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("Before: " +e));
}
问题是,为什么异常在一种情况下包装在
CompletionException
中,而在另一种情况下则不包装


更新:与错误报告相关。它已被标记为JDK中的一个bug并已解决。

似乎是JDK库中的一个bug

在“After”情况下,
.thencomose
向目标future添加一个
ThenCopy
完成节点,该节点的执行稍后由
.complete异常地
触发。完成节点的
run
方法在将来查找异常,并在目标上调用
.internalComplete
,将所有异常包装到
CompletionException
中。 查看节点是如何创建的,以及在何处进行换行

现在,在
Before
的情况下,代码路径完全不同。因为未来已经完成,
.thenCompose
不会创建其他节点,而是立即创建回调,并简单地返回一个(已完成的第二个未来),然后在该未来上调用
。whenComplete
,它同样不需要创建新的完成节点,只需立即,给它第二个未来的最初例外


孩子,看看这段代码,我想向我的学生们展示很多他们永远不应该做的事情……

你看过番石榴的未来列表了吗?如果您没有被锁定在框架中,我建议使用Guava的实现。在我看来,它的功能是完整的,而且更易于使用。Disclamer,我与谷歌没有任何关系。Completablefuture在并发兴趣列表上有很多流量。11月份的清单可能会让你感兴趣:为什么是bug?这种行为是否违反了任何合同?@MarkoTopolnik,目前还不清楚。这与dependent
CompletionStage
s有关。使用前缀为
然后为
的方法排列对单个阶段的依赖关系。如果一个阶段的计算突然终止,出现(未检查的)异常或错误,则所有需要完成的相关阶段也会异常完成,并且
CompletionException
将异常作为其原因。@MarkoTopolnik我没有看到任何地方明确提到所有异常,由未来抛出的将被包装成一个
CompletableException
,但我怀疑其目的是,它们有时被包装,有时不被包装,这取决于。。。什么?cpu速度?我的意思是,这种行为是不确定的。也许,“虫子”这个词太重了。让我们称之为“不受欢迎的功能”?谢谢,我将提交一个bug,我们将看到Oracle的反应。
After: java.util.concurrent.CompletionException: java.lang.RuntimeException
Before: java.lang.RuntimeException