CompletableFuture何时实际完成?

h.j*_*.k. 7 java completable-future

这是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);
}
Run Code Online (Sandbox Code Playgroud)

有点好奇,没有实际完成CompletableFutureallOf(...),例如调用它join(),我得到以下输出:

I'm called.
java.util.concurrent.CompletableFuture@<...>[Not completed, 1 dependents]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
Run Code Online (Sandbox Code Playgroud)

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

为了比较,我得到了预期的等待时间为5秒,并且当我实际上join()在最后进行时,我得到了以下输出.如果它有帮助,我在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);
Run Code Online (Sandbox Code Playgroud)

输出:

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]
Run Code Online (Sandbox Code Playgroud)

Sot*_*lis 7

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

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

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

呼叫allOf没有阻止.它将返回CompletableFuture它自己的一个,当CompletableFuture你完成所有你完成时它将完成.您链中到另一个CompletableFuturethenRun其中,因为r2r3完成,只需要看r1,即.它完成时r1完成.

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

Thread.sleep(6000);
Run Code Online (Sandbox Code Playgroud)

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


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

  • 为了完整起见,请注意您可以通过调用`ForkJoinPool.commonPool().awaitQuiescence(...)`来专门等待提交到公共池的任务的完成. (4认同)