繁体   English   中英

并行流中的 Fork-Join Pool

[英]Fork-Join Pool in parallel streams

我在网上搜索了各种文章和 Stack Overflow 问题,但我找不到完美的答案。 有许多问题与此接近,但略有不同。

我们知道 Java 8 Streams API 在内部使用 Fork-Join Pool。

现在我的问题是如何使用 Fork-Join 池划分流管道中的任务?

假设我们有以下内容:

List myList =  inputList.parallelStream().filter( x -> x>0 )
    .map(x -> x+100 ).collect(Collectors.toList());

现在我们有两种使用线程池划分任务的选项。

  1. filtermap作为单个任务并使用 fork-join 池运行它。
  2. filtermap作为两个不同的任务,并使用两个不同的 fork-join 线程池来运行它们。

我也知道流是延迟传播的,所以如果我们在两者之间有一个有状态的中间操作

List myList2 = inputList.parallelStream().filter( x -> x>0 )
    .map(x -> x+5 ).sorted().map(x -> x+5 ).collect(Collectors.toList());

那么如何创建线程池呢?

PS:之前就知道map功能是可以组合的。 我只是想为这个问题举个例子。

首先,您必须使用parallel才能使Fork-Join Pool处于活动状态。 这个答案稍微解释了Spliterator是如何执行拆分的; 但简单地说,拆分是使用流元素的源完成的,并且整个管道是并行处理的。 在您的示例中,它是filtermap (当然它也包括terminal操作)。

对于有状态操作 - 事情更复杂。 让我们以distinct为例,首先看看它如何处理顺序情况。

一般来说,您可能会认为使用HashSet可以实现non-parallel distinct - 您是正确的。 HashSet可以保存所有已经看到的值,并且根本不处理(发送到下一个操作)其他元素 - 从理论上讲,您可以使用非并行的distinct操作来完成。 但是如果已知StreamSORTED呢? 想想看,这意味着我们可以保留一个标记为seen元素(与之前的HashSet相反)。 基本上,如果你有:

 1,1,2,2,3

这意味着您的有状态操作可以在单个元素之上实现 - 而不是HashSet 代码将类似于:

T seen = null;
....
if(seen == null || (!currentElement.equals(seen)){
    seen = currentElement;
    // process seen;
}

但是这种优化只有当你知道流是SORTED时才有可能,因为这样你就知道下一个元素与你已经看到的元素相同,或者是一个新元素,这是你以前不可能看到的在其他一些先前的操作中 - 这由排序操作保证。

现在如何实现parallel distinct 你基本上问这个问题:

那么如何创建线程池

以同样的方式,从 Stream 的角度来看,没有任何变化, ForkJoinPool使用相同数量的线程 - 显然,唯一改变的是流实现。

简而言之,如果您的StreamORDERED则内部实现使用LinkedHashSet (实际上是它的多个实例,因为在这种情况下它确实减少了)来保留您的订单,如果您不关心订单,它会使用ConcurrentHashMap - 即如果源未排序(如Set )或者您使用了称为unordered显式。 如果您真的想知道它是如何完成的,您也可以查找sorted的实现。


所以底线是Fork Join Pool不会改变基于流的实现,它使用相同的模型。 另一方面,根据您拥有的操作,Stream API 可能会使用一些有状态数据进行有状态中间操作,例如HashSet/ConcurrentHashMap或单个元素等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM