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 线程池
简单 非常简单 较复杂
可控性
适合小功能 非常适合 一般
大型系统 不推荐滥用 推荐
自定义线程数 不方便 可以
隔离性

相关推荐
z落落1 天前
C# 多接口实现、重名成员、显式实现、接口继承+抽象类和接口区别
java·开发语言·c#
caimouse1 天前
Reactos 第 4 章 对象管理 — 4.6 对象的访问控制 / 4.7 句柄的遗传和继承
开发语言·windows·架构
C137的本贾尼1 天前
【实战】分析一张真实业务表的 InnoDB 存储结构
java·大数据·数据库
huangdong_1 天前
京东整店商品图片视频批量下载技术:从商品列表到自动分类
开发语言·python·音视频
超梦dasgg1 天前
亿级数据 不停服务平滑迁移(生产环境实战方案)
java·数据库
Zella折耳根1 天前
Java 正则表达式实战:IP 地址匹配与替换全解析
java·tcp/ip·正则表达式
摇滚侠1 天前
JavaWeb 全套教程 Filter 107-111
java·开发语言·servlet
YIN_尹1 天前
【Linux系统编程】基础IO第一讲——系统文件IO
android·java·linux·c++
聆风吟º1 天前
【C标准库】深入理解C语言 atoi 函数:字符串转换为整数
c语言·开发语言·库函数·atoi
凤山老林1 天前
81-Java Scanner 类
java·开发语言