Java 8实战-第七章

97 阅读3分钟

并行数据处理与性能

了解Stream接口如何不用太费力气就能对数据集执行并行操作。

并行流

并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

将顺序流转换为并行流

调用parallel方法,可以把流转换成并行流,也只需要对并行流调用sequential方法就可以编程顺序流,通过将两个方法结合起来,可以细化地控制在遍历流时哪些操作要并行执行,哪些要顺序执行。

测量流性能

并行化过程本身需要对流做递归划分,把每个子流的归纳操作分配到不同的线程,然后把这些操作的结果合并成一个值,但在多个内核之间移动数据的代价可能会比较大,所以很重要的一点是要保证在内核中并行执行工作的时间比在内核之间传输数据的时间长。

正确使用并行流

错用并行流而产生错误的首要原因,就是使用的算法改变了某些共享状态。

高效使用并行流

如果有疑问,测量,把顺序流转成并行流轻而易举,但却不一定是好事、留意自动装箱和拆箱会大大降低性能、有些类似limit和findFirst操作会在并行流上的性能就比顺序流差、还要考虑流的操作流水线的总计算成本等等考虑因素。

分支/合并框架

分支/合并框架的目的是以递归方式将可以并行的执行拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。

使用RecursiveTask

要把任务提交到这个池,必须创建RecursiveTask的一个子类。

    if(任务足够小或不可分) {
      顺序计算该任务
    } else {
    将任务分成两个子任务
    递归调用本方法,拆分每个子任务,等待所有子任务完成
    合并每个子任务的结果
    }

Spliterator

public interface Spliterator<T> {
  boolean tryAdvance(Consumer<? super T> action);
  Spliterator<T> trySplit();
  long estimateSize();
  int characteristics();
}

拆分过程

拆分过程受Spliterator本身特性影响,特性是通过characteristics方法声明的。该方法返回一个int,代表Spliterator本身特性集的编码。

实现自己的Spliterator

tryAdvance方法把String中当前位置的Character传给了Consumer,并让位置加一。 trySplit方法是Spliterator中最重要的一个方法,因为它定义了拆分要遍历的数据结构的逻辑。

小结

在本章中,你了解了以下内容。

内部迭代让你可以并行处理一个流,而无需在代码中显式使用和协调不同的线程。

虽然并行处理一个流很容易,却不能保证程序在所有情况下都运行得更快。并行软件的

行为和性能有时是违反直觉的,因此一定要测量,确保你并没有把程序拖得更慢。

像并行流那样对一个数据集并行执行操作可以提升性能,特别是要处理的元素数量庞大,或处理单个元素特别耗时的时候。

从性能角度来看,使用正确的数据结构,如尽可能利用原始流而不是一般化的流,几乎总是比尝试并行化某些操作更为重要。

分支/合并框架让你得以用递归方式将可以并行的任务拆分成更小的任务,在不同的线程上执行,然后将各个子任务的结果合并起来生成整体结果。

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