对于从事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并行流使用注意事项的更多相关文章

  1. 避坑 | Java8使用并行流(ParallelStream)注意事项

    示例分析 /** * 避坑 | Java8使用并行流(ParallelStream)注意事项 * * @author WH.L * @date 2020/12/26 17:14 */ public c ...

  2. Java8使用并行流(ParallelStream)注意事项

    Java8并行流ParallelStream和Stream的区别就是支持并行执行,提高程序运行效率.但是如果使用不当可能会发生线程安全的问题.Demo如下: public static void co ...

  3. 在使用Java8并行流时的问题分析

    最近在使用Java8的并行流时遇到了坑,线上排查问题时花了较多时间,分享出来与大家一起学习与自查 // 此处为坑 List<Java8Demo> copy = Lists.newArray ...

  4. Java8新特性 并行流与串行流 Fork Join

    并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流. Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作. Stream API 可以声明性地通过 para ...

  5. java8新特性——并行流与顺序流

    在我们开发过程中,我们都知道想要提高程序效率,我们可以启用多线程去并行处理,而java8中对数据处理也提供了它得并行方法,今天就来简单学习一下java8中得并行流与顺序流. 并行流就是把一个内容分成多 ...

  6. Java8新特性 - 并行流与串行流

    并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性地通过parallel()和 ...

  7. 三、并行流与串行流 Fork/Join框架

    一.并行流概念: 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性的通过pa ...

  8. Tomcat 应用中并行流带来的类加载问题

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/f-X3n9cvDyU5f5NYH6mhxQ作者:肖铭轩.王道环 随着 Java8 的不断流行, ...

  9. 【转】Java8 Stream 流详解

      当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...

随机推荐

  1. 使用session处理用户搜索后数据的上一页和下一页跳转

    搜索语句界面: /*单一检索:此处为一个下拉列表的检索*/ if(isset($_POST['submit']) && $_POST['submit'] == '点击搜索') { if ...

  2. web攻击技术与防护

    一.跨站脚本攻击(XSS) 跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器运行非法的HTML标签或JavaScript进行的一种攻击.动态创建的HTML部分有可能隐藏着安全漏洞.就这样, ...

  3. C#基础学习笔记(个人整理)

    学习笔记 第一章:c#基础 一.程序设计语言的发展及历史 1.程序设计语言? 通俗也叫编程语言,实现人与机器交互的工具 2.历史 1)机器语言 : 0,1 2)汇编语言 : 包含一些机器语言,同时增加 ...

  4. 路由器基础配置之单臂路由实现vlan间通信

    我们将以上面的拓扑图开始进行配置,目的为设置单臂路由实现vlan间通信,设置4个vlan,pc0,1,2为vlan10 pc3,4,5为vlan20:pc6,7,8为vlan30:server0,1为 ...

  5. C#基础-面向对象-多态

    多态,不同对象对同一方法的不同实现 使用abstract关键字表示抽象类 // 表示是一个抽象类 public abstract class Animal { private string name; ...

  6. 课时16.HTML-XHTML-HTML5区别(了解)

    简而言之 HTML语法非常宽松容错性强: XHTML更为严格,它要求标签必须小写,必须严格闭合,标签中的属性必须使用引号引起等等. HTML5是HTML的下一个版本所以除了非常宽松容错性强以外,还增加 ...

  7. Python__关于列表的引用 以append操作为例

    对于列表这样的可变类型来说,对它操作是不会改变内存地址的. 若列表里面存的元素是整数这样的不可变类型,若修改这个元素那地址还是会改变,如: >>> a = [,,] >> ...

  8. ABAP CDS - SELECT, WHERE

    格式 ... WHERE cond_expr ... 结果 定义CDS视图结果集的Where条件.访问CDS视图时,结果集仅包含来自数据源数据源的数据,该数据源数据源满足在where之后指定的条件co ...

  9. Hadoop常用高级特性

    HDFS HA HDFS HA(High Availability)高可用性 相同版本拷贝工具,分布式集群拷贝工具,使用MapReduce实现 DistCp Version2 Guide HFTP协议 ...

  10. 万年历Calendar、js修改日期

    //万年历 Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE,-1); //改变日期,改变年份.月份类似 SimpleDateF ...