8

这是MCVE

public static void main(String[] args) {
    CompletableFuture<String> r1 = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "41";
    });
    CompletableFuture<String> r2 = CompletableFuture.supplyAsync(() -> "42");
    CompletableFuture<String> r3 = CompletableFuture.supplyAsync(() -> {
        System.out.println("I'm called.");
        return "43";
    });
    CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println("End."); });
    Stream.of(r1, r2, r3).forEach(System.out::println);
}

有点奇怪,没有真正完成CompletableFuturefrom allOf(...),例如调用 its join(),我得到以下输出:

I'm called.
java.util.concurrent.CompletableFuture@<...>[Not completed, 1 dependents]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
java.util.concurrent.CompletableFuture@<...>[Completed normally]

我可以知道是什么导致 JVM 处理/认为r11 个(估计数量)依赖CompletableFuture,而它决定直接完成r2r3?我能看到的唯一区别就是try-catch,那么答案就这么简单吗?

作为比较,当我join()最后实际执行 a 时,我得到了 5 秒的预期等待时间和以下输出。如果有帮助,我会在 Java 8 Update 40 JVM 上遇到这个问题。

修改:

// ...
CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println("End."); }).join();
Stream.of(r1, r2, r3).forEach(System.out::println);

输出:

I'm called.
// <note: 5-second wait is here>
End.
java.util.concurrent.CompletableFuture@<...>[Completed normally]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
4

1 回答 1

7

r1并且r2CompletableFuture两个独立提交的异步任务。

我可以知道是什么导致 JVM 处理/认为 r1 有 1 个(估计数量)依赖 CompletableFuture,而它决定直接完成 r2 和 r3

它没有。当您println在这些实例上调用时,r2并且r3已经正常完成(它们并没有做太多)。r1没有(完成它的线程很可能正在休眠)。

调用allOf不会阻塞。它会返回一个CompletableFuture自己的,当CompletableFuture你给他们的所有东西都完成后,它就会完成。您将其链接到另一个CompletableFuturethenRun因为r2r3完成,仅取决于r1,即。完成时r1完成。

您选择放弃对此的引用,CompletableFuture但已安排提交的任务thenRun。如果你添加一个

Thread.sleep(6000);

在原始程序结束时,您将看到完成后End.打印的日志r1,因此,由thenRun.


请注意,除非另有说明,否则CompletableFuture(例如通过提交)中的异步任务都在使用守护线程supplyAsync的默认值内运行。ForkJoinPool您的应用程序将在 5 秒过去之前退出,除非您选择在某处阻塞并等待该时间过去。

于 2015-06-16T02:20:07.883 回答