CompletableFuture
的 Java 文档指出:
所有没有显式 Executor 参数的异步方法都使用 ForkJoinPool.commonPool() 执行(除非它不支持至少两个并行级别,在这种情况下,将创建一个新线程来运行每个任务)
决定为每个任务创建一个线程的原因是什么?为什么他们不选择创建一个至少有 2 个工作线程的池,或者为什么
commonPool()
返回的执行器没有至少 2 个工作线程?
为每个任务创建一个线程对我来说效率很低。
TL;DR:
commonPool
可以是同步的。它可以在调用 .join()
时直接在调用 join 的线程上执行任务,而不是安排任务在后台线程上运行。这种同步执行语义很容易导致死锁,从而违反异步方法的约定。 CompletableFuture
通过生成新线程来解决潜在的同步执行问题。
注意:具有一个后台线程的
commonPool
和没有后台线程的同步 commonPool
都报告 parallelism
为 1
。仅在后一种情况下才需要线程 hack,但没有尝试区分这两种情况。
参考:
此更改是在以下提交中进行的:
8020435:CompletableFuture/Basic.java 在单核机器上失败
https://github.com/openjdk/jdk/commit/b1a10b8ed7bedb27ae25341602319a11a1225ee7
当时的代码对于并行语义有什么说明:
公共池并行性。为了更简单的使用和管理 当公共池线程被禁用时,我们允许底层 common.parallelism 字段为零,但在这种情况下仍然报告 并行度为 1 以反映最终的调用者运行机制。