为啥parallelStream 使用ForkJoinPool,而不是普通的线程池?
Posted
技术标签:
【中文标题】为啥parallelStream 使用ForkJoinPool,而不是普通的线程池?【英文标题】:Why does parallelStream use a ForkJoinPool, not a normal thread pool?为什么parallelStream 使用ForkJoinPool,而不是普通的线程池? 【发布时间】:2020-08-29 03:22:35 【问题描述】:参考Java's Fork/Join vs ExecutorService - when to use which?,通常使用传统的线程池来处理很多独立的请求; ForkJoinPool
用于处理连贯/递归任务,其中一个任务可能会产生另一个子任务并稍后加入。
那么,为什么 Java-8 的 parallelStream
默认使用 ForkJoinPool
而不是传统的执行器?
在很多情况下,我们在stream()
或parallelStream()
之后使用forEach()
,然后提交一个函数式接口作为参数。在我看来,这些任务是独立的,不是吗?
【问题讨论】:
forEach
是十几个终端操作中的一个。即使您“在许多情况下”使用它,它也肯定不会驱动仍然必须支持所有这些的软件的设计决策。除此之外,我认为forEach
是最没用的终端操作,它经常导致开发人员编写损坏的“用另一个名字循环”代码。
【参考方案1】:
一个重要的事情是ForkJoinPool
也可以执行“正常”任务(例如Runnable
、Callable
),因此它不仅仅用于递归创建的任务。
另一个(重要的)事情是ForkJoinPool
有多个队列,一个用于每个工作线程的任务,一个普通的执行器(例如ThreadPoolExecutor
)只有一个。这对他们应该运行什么样的任务有很大影响。
普通执行器必须执行的任务越小越多,将任务分配给工作人员的同步开销就越高。如果大部分任务都很小,worker会经常访问内部任务队列,导致同步开销。
这就是ForkJoinPool
以其多个队列而大放异彩的地方。每个worker只是从自己的队列中获取任务,大部分时间不需要通过阻塞来同步,如果它是空的,它可以从另一个worker那里窃取任务,但是从队列的另一端,这也很少会导致同步开销,因为工作窃取应该是相当罕见的。
现在这与并行流有什么关系?流框架设计为易于使用。当您想轻松地将某些内容拆分为许多并发任务时,应该使用并行流,其中所有任务都相当小且简单。这就是ForkJoinPool
是合理选择的地方。它在大量较小的任务上提供了更好的性能,并且它可以处理更长的任务,如果必须的话。
【讨论】:
以上是关于为啥parallelStream 使用ForkJoinPool,而不是普通的线程池?的主要内容,如果未能解决你的问题,请参考以下文章
使用 parallelStream 时抛出 InterruptedException - Java [重复]
带有 spring 注释方法的 Java .parallelStream()
Java 8 的 parallelStream 中产生了多少线程?
java 8 parallelStream() 和 sorted()