持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
机制
使用并行流,可以有效利用计算机的多CPU硬件,提升逻辑的执行速度。并行流通过将一整个stream划分为多个片段,然后对各个分片流并行执行处理逻辑,最后将各个分片流的执行结果汇总成为一整个流。因为是并发,所以末端操作需要保证线程安全。
并行是怎么实现的
下面我们还是通过简单的demo来看看是怎么实现的:
package com.study.stream;
import java.util.ArrayList;
import java.util.List;
public class SeeParallelSourceDemo {
/**
* 并行筛选并统计信息
*
* @param list
* @return
*/
private static long parallelFilterAndCount(List<Integer> list) {
return list.stream().parallel().filter(i -> i % 2 == 0).count();
}
public static void main(String[] args) {
System.out.println(parallelFilterAndCount(new ArrayList<Integer>() {
{
add(1);
add(2);
add(3);
add(4);
add(5);
add(6);
}
}));
}
}
parallel
public final S parallel() {
sourceStage.parallel = true;
return (S) this;
}
可以看到parallel方法只是用来设置sourceStage.parallel为true。
中间操作
中间操作就不看了前文《Java8 Stream的底层是怎么实现的》介绍过,因为stream为懒执行,所以filter应该是一样的。
末端操作
因为部分代码在《Java8 Stream的底层是怎么实现的》已经介绍过,所以我们直奔主题。
- AbstractTask#isParallel
@Override
public final boolean isParallel() {
return sourceStage.parallel;
}
在前面的parallel方法中设置的
- AbstractTask#compute
@Override
public void compute() {
Spliterator<P_IN> rs = spliterator, ls; // right, left spliterators
long sizeEstimate = rs.estimateSize();
// 获取阈值大小
long sizeThreshold = getTargetSize(sizeEstimate);
boolean forkRight = false;
@SuppressWarnings("unchecked") K task = (K) this;
// 元素数量 > 阈值 rs可以进行分割
while (sizeEstimate > sizeThreshold && (ls = rs.trySplit()) != null) {
K leftChild, rightChild, taskToFork;
// 使用ls,rs构造两个新的任务
task.leftChild = leftChild = task.makeChild(ls);
task.rightChild = rightChild = task.makeChild(rs);
// 设置等待数量为1
task.setPendingCount(1);
if (forkRight) {
// 下次让leftChild fork
forkRight = false;
rs = ls;
task = leftChild;
taskToFork = rightChild;
}
else {
// 下次让rightChild fork
forkRight = true;
task = rightChild;
taskToFork = leftChild;
}
// 异步执行,递归
taskToFork.fork();
sizeEstimate = rs.estimateSize();
}
task.setLocalResult(task.doLeaf());
task.tryComplete();
}
就是对任务进行拆分,最终合并结果。这里的重点其实在于ForkJoinTask后面再出一篇专门的文章。