Java8并行流使用注意事项

对于从事Java开发的童鞋来说,相信对于Java8的并行流并不陌生,没错,我们常常用它来执行并行任务,但是由于并行流(parallel stream)采用的是享线程池,可能会对我们的性能造成严重影响,那怎么处理呢?
问题
首先我们来看看具体的问题。在开发中,我们常常通过以下方法,实现并行流执行并行任务:
myList.parallelStream.map(obj -> longRunningOperation())
但是这存在一个严重的问题:在 JVM 的后台,使用通用的 fork/join 池来完成上述功能,该池是所有并行流共享的。默认情况,fork/join 池会为每个处理器分配一个线程。假设你有一台16核的机器,这样你就只能创建16个线程。对 CPU 密集型的任务来说,这样是有意义的,因为你的机器确实只能执行16个线程。但是真实情况下,不是所有的任务都是 CPU 密集型的。例如:
myList.parallelStream
.map(this::retrieveFromA)
.map(this::processUsingB)
.forEach(this::saveToC)
myList.parallelStream
.map(this::retrieveFromD)
.map(this::processUsingE)
.forEach(this::saveToD)
这两个流很大程度上是受限于IO操作,所以会等待其他系统。但这两个流使用相同的(小)线程池,因此会相互等待而被阻塞,非常不友好。比如:
final List<Integer> firstRange = buildIntRange();
firstRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
在执行期间,我获取了一份线程dump的文件。这是相关的线程:
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
现在,我要并行的执行这两个并行流:
Runnable firstTask = () -> {
firstRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
};
Runnable secondTask = () -> {
secondRange.parallelStream().forEach((number) -> {
try {
// do something slow
Thread.sleep(5);
} catch (InterruptedException e) { }
});
};
// run threads
这次我们再看一下线程dump文件:
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
正如你所见,结果是一样的。我们只使用了4个线程。
解决办法
对于上面的问题,我们可以在JVM 后台使用 fork/join 池,在 ForkJoinTask 的文档中,我们可以看到:
如果合适,安排一个异步执行的任务到当前正在运行的池中。如果任务不在inForkJoinPool()中,也可以调用ForkJoinPool.commonPool()获取新的池来执行,比如:
ForkJoinPool forkJoinPool = new ForkJoinPool(3);
forkJoinPool.submit(() -> {
firstRange.parallelStream().forEach((number) -> {
try {
Thread.sleep(5);
} catch (InterruptedException e) { }
});
});
ForkJoinPool forkJoinPool2 = new ForkJoinPool(3);
forkJoinPool2.submit(() -> {
secondRange.parallelStream().forEach((number) -> {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
});
});
现在,我们再次查看线程池:
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-4
ForkJoinPool-2-worker-1
ForkJoinPool-2-worker-2
ForkJoinPool-2-worker-3
ForkJoinPool-1-worker-4
上面这种方法为什么又正确显示了呢?因为我们创建自己的线程池,所以可以避免共享线程池,如果有需要,甚至可以分配比处理机数量更多的线程。
ForkJoinPool forkJoinPool = new ForkJoinPool(<numThreads>);
以上就是Java8并行流在使用中所存在的一些问题及解决办法,部分内容参考自一个Java教学网站,希望对Java初学者有所帮助。
Java8并行流使用注意事项的更多相关文章
- 避坑 | Java8使用并行流(ParallelStream)注意事项
示例分析 /** * 避坑 | Java8使用并行流(ParallelStream)注意事项 * * @author WH.L * @date 2020/12/26 17:14 */ public c ...
- Java8使用并行流(ParallelStream)注意事项
Java8并行流ParallelStream和Stream的区别就是支持并行执行,提高程序运行效率.但是如果使用不当可能会发生线程安全的问题.Demo如下: public static void co ...
- 在使用Java8并行流时的问题分析
最近在使用Java8的并行流时遇到了坑,线上排查问题时花了较多时间,分享出来与大家一起学习与自查 // 此处为坑 List<Java8Demo> copy = Lists.newArray ...
- Java8新特性 并行流与串行流 Fork Join
并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流. Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作. Stream API 可以声明性地通过 para ...
- java8新特性——并行流与顺序流
在我们开发过程中,我们都知道想要提高程序效率,我们可以启用多线程去并行处理,而java8中对数据处理也提供了它得并行方法,今天就来简单学习一下java8中得并行流与顺序流. 并行流就是把一个内容分成多 ...
- Java8新特性 - 并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性地通过parallel()和 ...
- 三、并行流与串行流 Fork/Join框架
一.并行流概念: 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性的通过pa ...
- Tomcat 应用中并行流带来的类加载问题
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/f-X3n9cvDyU5f5NYH6mhxQ作者:肖铭轩.王道环 随着 Java8 的不断流行, ...
- 【转】Java8 Stream 流详解
当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...
随机推荐
- linux命令详解-useradd,groupadd
linux命令详解-useradd,groupadd 我们在linux命令行中输入useradd: Options: -b, --base-dir BASE_DIR base direc ...
- 转:比较spring cloud和dubbo,各自的优缺点是什么
原文:https://blog.csdn.net/u010664947/article/details/80007767 dubbo由于是二进制的传输,占用带宽会更少 springCloud是http ...
- mysql 命令 小结
CREATE DATABASE IF NOT EXISTS yourdbname DEFAULT CHARSET utf8 COLLATE utf8_general_ci;创建中文数据库show gl ...
- LeetCode970. 强整数
问题:970. 强整数 用户通过次数0 用户尝试次数0 通过次数0 提交次数0 题目难度Easy 给定两个非负整数 x 和 y,如果某一整数等于 x^i + y^j,其中整数 i >= 0 且 ...
- JAVA / MySql 编程——第二章 初始MySQL
1. MySQL: ● MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL最流行的关系型数据库管理系统, ...
- Vue项目部署遇到的问题及解决方案
写在前面 Vue-Router 有两种模式,默认是 hash 模式,另外一种是 history 模式. hash:也就是地址栏里的 # 符号.比如 http://www.example/#/hello ...
- 用php读取xml数据
parser是php内置的一个用来处理xml的解析器,它的工作由三个事件组成:起始标签. 读取数据.结束标签. 也就是说在对xml进行处理的时候每当遇到起始标签.数据和结束标签的时候函数会做相应的动作 ...
- 进程、线程、协程和GIL(二)
上一篇博客讲了进程.线程.协程和GIL的基本概念,这篇我们来说说在以下三点: 1> python中使用threading库来创建线程的两种方式 2> 使用Event对消来判断线程是否已启动 ...
- Hadoop环境搭建 (伪分布式搭建)
一,Hadoop版本下载 建议下载:Hadoop2.5.0 (虽然是老版本,但是在企业级别中运用非常稳定,新版本虽然添加了些小功能但是版本稳定性有带与考核) 1.下载地址: hadoop.apache ...
- CodeForces 785C Anton and Fairy Tale 二分
题意: 有一个谷仓容量为\(n\),谷仓第一天是满的,然后每天都发生这两件事: 往谷仓中放\(m\)个谷子,多出来的忽略掉 第\(i\)天来\(i\)只麻雀,吃掉\(i\)个谷子 求多少天后谷仓会空 ...