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 非常类似.但是 ...
随机推荐
- MySQL事务隔离级别 解决并发问题
MySQL事务隔离级别 1. 脏读: 骗钱的手段, 两个窗口或线程分别调用数据库转账表,转账后未提交,对方查看到账后,rollback,实际钱没转. 演示方法: mysql默认的事务隔离级别为repe ...
- java基础 File 递归删除文件夹中所有文件文件夹 目录(包含子目录)下的.java文件复制到e:/abc文件夹中, 并统计java文件的个数
File 递归删除文件夹中所有文件文件夹 package com.swift.kuozhan; import java.io.File; import java.util.Scanner; /*键盘录 ...
- Map the Debris -freecodecamp算法题目
Map the Debris 1.要求 返回一个数组,其内容是把原数组中对应元素的平均海拔转换成其对应的轨道周期. 原数组中会包含格式化的对象内容,像这样 {name: 'name', avgAlt: ...
- Exception occurred during processing request: The given object has a null identifier: com.zsn.crm.Model.SaleVisit; nested exception is org.hibernate.TransientObjectException: The given object has a nu
edit.jsp页面没有加入隐藏字段 id ,导致模型驱动封装时缺少id ,,调用update更新数据库时出错!
- 懒下载软件,一行代码连接wifi^_^
按键盘的windows+R,输入cmd,回车键 设置语句netsh wlan set hostednetwork mode=allow ssid=user key=possword 按回车键 启动语句 ...
- python获取Excel数据
Python中一般使用xlrd(excel read)来读取Excel文件,使用xlwt(excel write)来生成Excel文件(可以控制Excel中单元格的格式),需要注意的是,用xlrd读取 ...
- PHP 二维数组按某一个键值排序
一.前言 在某个项目中,需要读取某个文件夹下的所有文件,在本地的 Windows 环境下时,读取出来的二维数组的文件名称和在 Windows 文件夹的文件排序一致, 但是项目上线后,环境为 Linux ...
- 40.VUE学习之--组件之间的数据传参父组件向子组件里传参,props的使用实例操作
父组件向子组件里传参,props的使用实例 <!DOCTYPE html> <html> <head> <meta charset="utf-8&q ...
- Python入门必知的几个点
Python是Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言.全世界差不多有600多种编程语言,但流行的编程语言也就那么20来种.如果你听说过TIOB ...
- C# 设定弹出窗体位置
一.C#中弹出窗口位置 加入命名空间 using System.Drawing using System.Windows.Forms 假定窗口名为form1,则 //窗体位置在屏幕中间 form1.S ...