一、 前言
在说 parallelStream 之前, 一定要了解 Stream 以及它的基本操作
二、什么是 ParallelStream
上文讲到的 Java8 Stream 流在执行时候是串行化的, 如果说任务执行的耗时比较长, 可以使用 Stream 的 “兄弟流” ParallelStream
防止误导, 并非耗时就一定要使用并行, 根据不同的业务场景, 合理的使用即可
parallelStream 是一种并行流, 意思为处理任务时并行处理, 这里和并发编程就有了千丝万缕的关系
前提是硬件支持, 如果单核 CPU, 只会存在并发处理, 而不会并行
这篇文章主要是说明 ParallelStream 其中一个可能为成为埋雷的点
项目中业务使用的并行流真的会都并行处理么?
三、如何使用 ParallelStream
ParallelStream 在使用上与 Stream 无区别, 本质返回的都是一个流, 只不过底层处理时 根据条件判断是并行或者是串行
[图片上传失败…(image-b06783-1604490624520)]
并行流并不会按照原本的顺序轨迹执行, 而是 随机执行, 当然对于这种 forEach 输出也可以做到顺序串行, 但这个不在文章中的重点
四、ForkJoinPool
相信如果在项目中实际使用过并行流的小伙伴, 一定会知道 ForkJoinPool
没错, 并行流底层就是使用的 ForkJoinPool, 一种 工作窃取算法线程池
ForkJoinPool 的优势在于, 可以充分利用多 CPU 的优势,把一个任务拆分成多个"小任务", 把多个"小任务"放到多个处理器核心上并行执行; 当多个"小任务"执行完成之后, 再将这些执行结果合并起来
五、并行流的陷阱
5.1 线程安全问题
只要是并行处理, 如果在流程中的操作产生了竞态条件, 就会存在线程安全问题
这里举个例子进行说明具体问题
public static void main(String[] args) {
List<Integer> integerList = Lists.newArrayList();
List<String> strList = Lists.newArrayList();
int practicalSize = 1000000;
for (int i = 0; i < practicalSize; i++) {
strList.add(String.valueOf(i));
}
strList.parallelStream().forEach(each -> {
integerList.add(Integer.parseInt(each));
});
log.info(" >>> integerList 预计长度 :: {}", practicalSize);
log.info(" >>> integerList 实际长度 :: {}", integerList.size());
}
/**
* >>> integerList 预估长度 :: 1000000
* >>> integerList 实际长度 :: 211195
*/
上面这段程序运行流程说明如下:
1、创建了两个 List, 分别是 String、Integer 类型
2、向 strList 插入 1000000 条记录
3、使用并行流将 strList 中的数据格式化为 Integer 并添加到 integerList
4、输出 integerList