为什么 parallelStream 使用 ForkJoinPool,而不是普通的线程池?

Why does parallelStream use a ForkJoinPool, not a normal thread pool?

参考Java's Fork/Join vs ExecutorService - when to use which?,传统的线程池通常用于处理很多独立的请求; ForkJoinPool 用于处理 coherent/recursive 任务,其中一个任务可能会产生另一个子任务并稍后加入。

那么,为什么Java-8的parallelStream默认使用ForkJoinPool而不是传统的执行器呢?

在很多情况下,我们在stream()parallelStream()之后使用forEach(),然后提交一个功能接口作为参数。在我看来,这些任务是独立的,不是吗?

一件重要的事情是 ForkJoinPool 也可以执行“正常”任务(例如 RunnableCallable),因此它不仅仅意味着递归地使用-创建任务。

另一件(重要的)事情是 ForkJoinPool 有多个队列,每个工作线程一个,用于任务,而普通执行程序(例如 ThreadPoolExecutor)只有一个。这对他们应该执行什么样的任务有很大影响 运行。

一个普通的executor要执行的任务越小越多,分配任务给worker的同步开销就越大。如果大部分任务都很小,worker会经常访问内部任务队列,导致同步开销。

这里是 ForkJoinPool 的多个队列的亮点所在。每个worker只是从自己的队列中拿任务,大部分时间不需要通过阻塞来同步,如果它是空的,它可以从另一个worker那里窃取任务,但是从队列的另一端,这也很少会导致同步开销,因为工作窃取应该很少见。

现在这与并行流有什么关系?流框架旨在易于使用。当您想轻松地将某些内容拆分为许多并发任务时,应该使用并行流,其中所有任务都相当小且简单。这是 ForkJoinPool 是合理选择的地方。它在大量较小的任务上提供了更好的性能,并且 可以 处理更长的任务,如果必须的话。