前言
在学习过程中遇到 parallelStream
流操作,学习记录一下。
实现
理论部分
此前总结过 stream
流的操作,链接。parallelStream 方法的使用与 stream 相同,只是在创建 stream 流时调用 parallelStream 方法。parallelStream 把 stream 流转换为并行流,充分利用了多核处理器的性能。
需要格外注意的是:由于 paralleStream 是并行操作,需要考虑到可能因此产生的并发问题。
但 paralleStream 的性能并不一定比 stream 更好,在以下场景下 parallelStream 的性能会比 stream 更低:
-
数据量较小:并行流的并行处理能力是通过将数据分成多个部分并在多个线程上同时处理来实现的。如果数据量太小,分成的部分太少,那么并行流的并行处理能力就无法发挥出来,反而会增加线程切换的开销,导致性能下降。
-
计算量较小:并行流的并行处理能力主要体现在计算密集型的操作上,例如排序、聚合等。如果操作的计算量较小,那么并行流的并行处理能力就无法发挥出来,反而会增加线程切换的开销,导致性能下降。
-
IO密集型操作:并行流的并行处理能力主要体现在计算密集型的操作上,对于IO密集型的操作,例如文件读写、网络通信等,并行流的并行处理能力就无法发挥出来,反而会增加线程切换的开销,导致性能下降。
-
数据依赖:并行流的并行处理能力是通过将数据分成多个部分并在多个线程上同时处理来实现的。如果操作之间存在数据依赖,那么就无法将数据分成多个部分并在多个线程上同时处理,从而无法发挥并行流的并行处理能力,导致性能下降。
-
不可变数据:并行流的并行处理能力主要体现在对可变数据的操作上,例如对List、Map等可变数据的操作。如果操作的数据是不可变的,那么就无法发挥并行流的并行处理能力,导致性能下降。
-
线程切换开销:并行流的并行处理能力是通过将数据分成多个部分并在多个线程上同时处理来实现的。如果线程切换的开销较大,那么就无法发挥并行流的并行处理能力,导致性能下降。
实践部分
parallelStream 的使用与 stream 基本相同,例如以下代码:
@GetMapping("/t10")
public void test10(){
List<Integer> numbers1 = new ArrayList<>();
List<Integer> numbers2 = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
numbers1.add(RandomUtil.randomInt(1,1000000));
numbers2.add(RandomUtil.randomInt(1,1000000));
}
final long startTime1 = DateUtil.current();
List<Integer> sortedNumbers1 = numbers1.stream()
.map(o -> o * RandomUtil.randomInt(1, 1000000))
.collect(Collectors.toList());
System.out.println("stream流耗时:" + (DateUtil.current()-startTime1));
final long startTime2 = DateUtil.current();
List<Integer> sortedNumbers2 = numbers1.parallelStream()
.map(o -> o * RandomUtil.randomInt(1, 1000000))
.collect(Collectors.toList());
System.out.println("parallelStream流耗时:" + (DateUtil.current()-startTime2));
}
调用接口的三次输出:
# 此时可以看到并行流的性能要好于 stream 流
stream流耗时:88
parallelStream流耗时:44
stream流耗时:41
parallelStream流耗时:16
stream流耗时:123
parallelStream流耗时:51
总结
学习记录下 paralleStream 并行流。