这里是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);
}
有点奇怪,没有真正完成CompletableFuture
的{{1}},例如调用它的allOf(...)
,我得到以下输出:
join()
我是否知道导致JVM处理/认为I'm called.
java.util.concurrent.CompletableFuture@<...>[Not completed, 1 dependents]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
java.util.concurrent.CompletableFuture@<...>[Completed normally]
有1 (estimated number of) dependent CompletableFuture
的原因,而它决定直接完成r1
和r2
?我能看到的唯一区别就是r3
,答案就这么简单了吗?
为了进行比较,我得到了预期的5秒等待时间以及当我在结尾处实际进行try-catch
时的以下输出。如果有帮助,我会在Java 8 Update 40 JVM上遇到这种情况。
修饰:
join()
输出:
// ...
CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println("End."); }).join();
Stream.of(r1, r2, r3).forEach(System.out::println);
答案 0 :(得分:6)
r1
和r2
为CompletableFuture
。
我可以知道是什么导致JVM对待/认为r1有1 (估计数量)依赖CompletableFuture,而它决定 直截了当地完成r2和r3
没有。当您在这些实例上调用println
时,r2
和r3
已正常完成(它们没有做太多工作)。 r1
没有(已经完成它的线程很可能正在睡觉)。
对allOf
的调用没有阻止。它将返回自己的CompletableFuture
,当您完成所有CompletableFuture
时,它将完成。您将其链接到另一个CompletableFuture
thenRun
,其中r2
和r3
完成后,只需依赖r1
,即。它在r1
完成时完成。
您选择放弃对此CompletableFuture
的引用,但已安排提交给thenRun
的任务。如果添加
Thread.sleep(6000);
在原始程序结束时,您会在End.
完成时看到您的r1
日志,因此,thenRun
返回的日志会打印出来。
请注意,除非另有说明,否则CompletableFuture
中的异步任务(例如,通过supplyAsync
提交)都在使用守护程序线程的默认ForkJoinPool
内运行。您的应用程序将在5s过去之前退出,除非您选择阻止某个地方并等待该时间过去。