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



参考Java的Fork/join与ExecutorService——何时使用哪个?,传统的线程池通常用于处理许多独立的请求;ForkJoinPool用于处理连贯/递归任务,其中任务可能会生成另一个子任务并在以后加入它。

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

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

一件重要的事情是,ForkJoinPool可以执行"正常"任务(例如RunnableCallable( 也是如此,因此它不仅适用于递归创建的任务。

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

普通执行程序必须执行的任务越小、越多,将任务分配给工作线程的同步开销就越高。如果大多数任务都很小,则工作线程将经常访问内部任务队列,这会导致同步开销。

这就是ForkJoinPool的多个队列大放异彩的地方。每个工作线程只是从自己的队列中获取任务,大多数时候不需要通过阻塞来同步,如果它是空的,它可以从另一个工作线程那里窃取任务,但从队列的另一端,这也很少导致同步开销,因为工作窃取应该是相当罕见的。

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

最新更新