CompletableFuture 什么时候真正完成?
When is CompletableFuture actually completed?
这是 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);
}
有点奇怪,没有真正完成 allOf(...)
的 CompletableFuture
,例如调用它的 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 treat/think r1
有 1 (estimated number of) dependent CompletableFuture
,而它决定直接完成 r2
和 r3
?我能看到的唯一区别就是 try-catch
,那么答案就这么简单吗?
为了比较,当我最后实际执行 join()
时,我得到了 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]
r1
和 r2
是两个独立提交的异步任务的 CompletableFuture
。
May I know what's causing the JVM to treat/think that r1 has 1
(estimated number of) dependent CompletableFuture, while it decides to
straightforwardly complete r2 and r3
没有。当您在这些实例上调用 println
时,r2
和 r3
已经正常完成(它们做的不多)。 r1
没有(本应完成它的线程很可能正在休眠)。
对 allOf
的调用没有阻塞。它将 return 自己的 CompletableFuture
完成,当你给他们的所有 CompletableFuture
完成后。你将它链接到另一个 CompletableFuture
和 thenRun
,因为 r2
和 r3
已经完成,所以只依赖于 r1
,即。 r1
完成时完成。
您选择放弃对此 CompletableFuture
的引用,但已安排提交给 thenRun
的任务。如果您添加
Thread.sleep(6000);
在原始程序的末尾,您会看到 End.
日志在 r1
完成时打印出来,因此 return 由 thenRun
编辑.
请注意,除非另有说明,您在 CompletableFuture
中的异步任务(例如通过 supplyAsync
提交)都是 运行 在默认 ForkJoinPool
中使用守护线程。您的应用程序将在 5 秒过去之前退出,除非您选择阻塞某处并等待该时间过去。
这是 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);
}
有点奇怪,没有真正完成 allOf(...)
的 CompletableFuture
,例如调用它的 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 treat/think r1
有 1 (estimated number of) dependent CompletableFuture
,而它决定直接完成 r2
和 r3
?我能看到的唯一区别就是 try-catch
,那么答案就这么简单吗?
为了比较,当我最后实际执行 join()
时,我得到了 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]
r1
和 r2
是两个独立提交的异步任务的 CompletableFuture
。
May I know what's causing the JVM to treat/think that r1 has 1 (estimated number of) dependent CompletableFuture, while it decides to straightforwardly complete r2 and r3
没有。当您在这些实例上调用 println
时,r2
和 r3
已经正常完成(它们做的不多)。 r1
没有(本应完成它的线程很可能正在休眠)。
对 allOf
的调用没有阻塞。它将 return 自己的 CompletableFuture
完成,当你给他们的所有 CompletableFuture
完成后。你将它链接到另一个 CompletableFuture
和 thenRun
,因为 r2
和 r3
已经完成,所以只依赖于 r1
,即。 r1
完成时完成。
您选择放弃对此 CompletableFuture
的引用,但已安排提交给 thenRun
的任务。如果您添加
Thread.sleep(6000);
在原始程序的末尾,您会看到 End.
日志在 r1
完成时打印出来,因此 return 由 thenRun
编辑.
请注意,除非另有说明,您在 CompletableFuture
中的异步任务(例如通过 supplyAsync
提交)都是 运行 在默认 ForkJoinPool
中使用守护线程。您的应用程序将在 5 秒过去之前退出,除非您选择阻塞某处并等待该时间过去。