1.将顺序流转换为并行流(生成1 - n,转为并行流)

Java 并行调接口 java并行调用_数据结构


并行归纳操作

Java 并行调接口 java并行调用_数据结构_02


如果想将并行流转为顺序流,可以使用sequential方法。流水线会并行执行,因为最后调用的是它。

Java 并行调接口 java并行调用_Java 并行调接口_03


最后一次parallel或sequential调用会影响整个流水线。

并行流内部使用了默认的ForkJoinPool,它默认的线程数量就是你的处理器数量。可以通过java.util.concurrent.ForkJoinPool.common. parallelism来改变线程大小

共享可变状态会影响并行流以及并行计算,下面的代码每次执行结果都不一样。

Java 并行调接口 java并行调用_数据结构_04


Java 并行调接口 java并行调用_Java 并行调接口_05


高效的使用并行流:1.留意装箱,装箱拆箱会大大降低性能,可以使用原始类型流IntStream等。2.有些操作在并行流上性能就很差。limit、findFirst等依赖元素的顺序性。findAny比findFirst性能高,因为不用按顺序执行。可以调用unordered方法把有序流变为无序流。3.

考虑背后的数据结构是否易于分解。ArrayList比LinkedList易于分解。4.考虑终端合并步骤的代价大小。

Java 并行调接口 java并行调用_Java 并行调接口_06

分支合并框架Fork/JOIN

分支/合并框架的目的是以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。它是ExecutorService接口的一个实现,它把子任务分配给线程池(称为ForkJoinPool)中的工作线程。

RecursiveTask任务

要把任务提交到这个池,必须创建RecursiveTask的一个子类,其中R是并行化任务(以 及所有子任务)产生的结果类型,或者如果任务不返回结果,则是RecursiveAction类型。需要实现protected abstract R compute();方法,该方法需要同时定义了将任务拆分成子任务的逻辑,以及无法再拆分或不方便再拆分时,生成单个子任务结果的逻辑。

Java 并行调接口 java并行调用_数据结构_07


分支/合并过程

Java 并行调接口 java并行调用_Java 并行调接口_08


代码示例

Java 并行调接口 java并行调用_递归_09


使用多个ForkJoinPool实例是没什么意义的,一般来说实例一次,把实例保存在静态字段中。

Java 并行调接口 java并行调用_数据结构_10


对一个任务调用join方法会阻塞调用方,直到该任务做出结果。因此,有必要在两个子任务的计算都开始之后再调用它。否则,每个子任务都必须等待另一个子任务完成才能启动。

不应该在RecursiveTask内部使用ForkJoinPool的invoke方法。相反,你应该始终直接调用compute或fork方法,只有顺序代码才应该用invoke来启动并行计算。

对子任务调用fork方法可以把它排进ForkJoinPool。同时对左右两边的子任务调用它似乎很自然,但这样做效率要比直接对其中一个调用compute低。这样做你可以为将其中一个子任务重用同一线程,从而避免在线程池中多分配一个任务造成的开销。

分支/合并框架需要“预热 ”或者说要执行几遍才会被JIT编译器优化。编译器内置的优化可能会为顺序版本带来一些优 (例如执行死码分析,去掉从未被使用的计算)。分支/合并框架工程用一种称为工作 取(work stealing)的技术来解决这个问题。在实际应 用中,这意味着这些任务差不多被平均分配到ForkJoinPool中的所有线程上。每个线程都为分 配给它的任务保存一个双向链式队列,每完成一个任务,就会从队列头上取出下一个任务开始执行。当某个线程完成自己队列上的任务时,会从其他双向队列的尾部取出任务帮忙执行。

Java 并行调接口 java并行调用_数据结构_11


Spliterator自动拆分流

Spliterator接口,和Iterator一样,Spliterator也用于遍历数据源中的元素

Java 并行调接口 java并行调用_数据结构_12


T是Spliterator遍历元素的类型,tryAdvance方法的行为类似普通的 Iterator,因为它会按顺序一个一个使用Spliterator中的元素,并且如果还有其他元素要遍历就返回true。但trySplit是专为Spliterator接口设计的,因为它可以把一些元素划出去分给第二个Spliterator(由该方法返回),让他们两个并行处理。Spliterator还可通过 estimateSize方法估计还 下多少元素要遍历,因为即使不那么确 ,能快速算出来是一个值 也有助于让拆分均匀一点。拆分过程

将Stream 分成多个部分的算法是一个递归过程。第一步是对第一个 Spliterator调用trySplit,生成第二个Spliterator。第二步对这两个Spliterator调用 trysplit,这样总共就有了四个Spliterator。这个框架不断对Spliterator调用trySplit 直到它返回null,表明它处理的数据结构不能再分。最后,这个递归拆分过程到第四步就终止了,这时所有的Spliterator在调用trySplit时都返回了null。

Java 并行调接口 java并行调用_递归_13


Spliterator接口声明的最后一个抽象方法是characteristics,它将返回一个int,代表Spliterator本身特性集的编码。

Java 并行调接口 java并行调用_递归_14


内部迭代可以并行处理一个流,当流无法正确并行时,需要去实现一个Spliterator。代码中不用显示使用线程。

并行流,需要我们使用正确合理的数据结构。

fork/join框架是递归的将任务拆分,并将结果汇总。

Spliterator定义了并行流如何拆分它要遍历的数据。