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 非常类似.但是 ...
随机推荐
- thymeleaf单选回显,多选回显,选回显,下拉默认选中第一个
//默认选中第一个<input type ="radio" name="repaymentType" th:each ="repaymentTy ...
- Orcale(一)
oracle数据库基本语句查询 ORacle-12560:TNS 协议配置器错误 1.服务:1.监听服务未开启 2.服务器未开启 3.环境变量:Oracle_sid = orcl 4.regedit注 ...
- PHP 基础知识总结
PHP 代表 PHP: Hypertext Preprocessor PHP 文件可包含文本.HTML.JavaScript代码和 PHP 代码 PHP 代码在服务器上执行,结果以纯 HTML 形式返 ...
- http网络协议 学习摘要
一:HTTP协议状态码 状态码主要用于描述当客户端向服务器发送请求时的返回结果,标记服务端的处理是否正常,通知出现的错误等工作. 状态码整体分为五大类: 1开头的状态码:信息类状态码,主要接收请求,表 ...
- php实现当前页面点击下载文件的实例
php控制器中代码 public function downFile($path = ''){ if(!$path) header("Location: /"); ...
- iOS-修改modal出来的控制器的大小
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ static BOOL sh ...
- url_maneger.py
coding=UTF-8 # url管理器 class urlManeger: def __init__(self): self.new_urls = set() self.old_urls = se ...
- HTML中body相关标签-03
今日主要内容: 列表标签 <ul>.<ol>.<dl> 表格标签 <table> 表单标签 <fom> 一.列表标签 列表标签分为三种. 1 ...
- CSS3 Flexbox(伸缩盒/弹性盒模型)可视化指南
在http://css.doyoe.com/(CSS参考手册)中,本文对应其中的伸缩盒 引入 Flexbox布局官方称为CSS Flexible Box Layout Module是一个CSS3新的布 ...
- Servlet过滤器---编码转换过滤器
该实例用于将请求与相应的编码设置为当前网站的默认编码 java类: import java.io.IOException; import javax.servlet.Filter; import ja ...