Java parallelStream并行流

目录

前言

parallelStream() 是 Java 8 Stream API 提供的 并行流(Parallel Stream)

它可以让:

  • 一个集合的处理逻辑
  • 自动使用多线程并行执行

而不是单线程。


一、最简单理解

普通 stream:

java 复制代码
list.stream()

是 单线程

例如:

java 复制代码
1个线程处理20万数据

parallelStream:

java 复制代码
list.parallelStream()

是 多线程并行处理

例如:

text 复制代码
8核CPU
可能8个线程同时处理

二、举个最简单例子

普通 stream :

java 复制代码
list.stream()
    .map(x -> doSomething(x))
    .collect(Collectors.toList());

执行过程:

text 复制代码
第1条
第2条
第3条
...

按顺序一个个执行。


parallelStream :

java 复制代码
list.parallelStream()
    .map(x -> doSomething(x))
    .collect(Collectors.toList());

执行过程:

text 复制代码
线程1 -> 第1万条
线程2 -> 第2万条
线程3 -> 第3万条
...

同时执行。


三、parallelStream底层是什么?

底层使用: ForkJoinPool

默认:

text 复制代码
CPU核心数 - 1

例如:

8核CPU:

text 复制代码
默认7个工作线程

四、parallelStream什么时候适合?

适合:CPU密集型任务

例如:

  • 大量计算
  • 加密
  • 哈希
  • 复杂规则计算
  • 图片处理
  • 金额计算
  • 风控计算

例如:

java 复制代码
list.parallelStream()
    .map(this::calculate)

计算 很耗CPU。这时收益巨大。


五、什么时候不适合?

IO密集型

例如:

text 复制代码
数据库
Feign
HTTP
Redis
文件

例如:

java 复制代码
list.parallelStream()
    .forEach(x -> feign.call());

通常会更慢,甚至打爆数据库/接口。


六、为什么IO场景不适合?

因为,parallelStream 默认线程数:

text 复制代码
CPU核数

而IO:

text 复制代码
大量时间在等待

CPU其实没干活。


比如:

text 复制代码
线程1 等数据库
线程2 等网络
线程3 等Redis

CPU闲着。

所以:

parallelStream 对IO提升有限。


七、parallelStream的优势

1. 开发简单

不用:

text 复制代码
线程池
Future
CountDownLatch
CompletableFuture

一行搞定。


2. 自动线程调度

自动:

  • 分片
  • 分任务
  • 合并结果

3. CPU利用率高

例如:

8核CPU:

text 复制代码
单线程只用了1核

parallelStream:

text 复制代码
8核一起跑

八、parallelStream的缺点

很多人乱用。


缺点1:线程不受控

默认:

text 复制代码
ForkJoinPool.commonPool()

全局共享线程池。

可能:

text 复制代码
影响整个系统

缺点2:不适合数据库/Feign

例如:

java 复制代码
parallelStream()
    .forEach(x -> mapper.insert(x));

危险。

可能 瞬间几千SQL。


缺点3:线程安全问题

例如:

你这样写:

java 复制代码
List<String> result = new ArrayList<>();

list.parallelStream().forEach(x -> {
    result.add(x);
});

错误!

因为:

text 复制代码
ArrayList线程不安全

可能:

  • 数据丢失
  • 数组越界
  • ConcurrentModificationException

正确写法

用:

java 复制代码
collect(Collectors.toList())

因为 collect 内部处理了并发。


九、parallelStream为什么有时反而更慢?

因为:线程切换有成本

例如:

java 复制代码
1+1

这种极小任务。

还没切线程快。


所以:数据量小不要用

经验:

数据量 建议
<1000 通常没必要
1万+ 开始有收益
10万+ 常有明显收益

十、parallelStream不保证顺序

例如:

java 复制代码
list.parallelStream()
    .forEach(System.out::println);

输出:

text 复制代码
乱序

如果需要顺序:

java 复制代码
forEachOrdered()

但:性能会下降。


十一、真正高性能方案(推荐)

很多大系统:

不会直接用:

text 复制代码
parallelStream

而是:

自定义线程池 + CompletableFuture

因为:

  • 可控
  • 可监控
  • 可限流
  • 可隔离

例如:

java 复制代码
ExecutorService pool = Executors.newFixedThreadPool(8);

CompletableFuture.supplyAsync(() -> {
    return calculate();
}, pool);

十二、parallelStream vs 线程池

对比 parallelStream 线程池
简单 非常简单 较复杂
可控性 差 强
适合小功能 非常适合 一般
大型系统 不推荐滥用 推荐
自定义线程数 不方便 可以
隔离性 差 强

相关推荐
1-1=01 小时前
C# 委托与事件
开发语言·c#
Fuyo_11191 小时前
C++ 运行时多态与虚函数机制全面剖析【面试重点】
开发语言·c++·面试
w_zero_one2 小时前
递归算法(一)
java·开发语言·算法
SL_staff2 小时前
私有化低代码不是减配版开发:JVS双模式建模如何重建IT交付信任
java·低代码·spring cloud
墨着染霜华3 小时前
Redocly 生成 OpenAPI 接口文档踩坑实录|Node 版本兼容 + 接口地址错误问题解决
java
文丑颜不良啊3 小时前
LeetCode1881~1990题目解析
java·leetcode
Julien20043 小时前
管理机密(一)
运维·服务器·开发语言·学习方法
Chen_harmony3 小时前
七、STL与string类
开发语言·c++
沐晓时光3 小时前
C语言入门,字符串和字符串函数知识讲解
c语言·开发语言
xxxiugou1233 小时前
C++命名空间与引用全解析
开发语言·c++