java parallelstream_Java8 parallelStream浅析

java parallelstream_Java8 parallelStream浅析JAVA8 中引入了 lamda 表达式和 Stream 接口 其丰富的 API 及强大的表达能力极大的简化代码 提升了效率 同时还通过 parallelStre 提供并发操作的支持 本文探讨 parallelStre 方法的使用 首先看下 javadoc 中对 parallelStre 的定义 Asequenceofe

JAVA8中引入了lamda表达式和Stream接口。其丰富的API及强大的表达能力极大的简化代码,提升了效率,同时还通过parallelStream提供并发操作的支持,本文探讨parallelStream方法的使用。

首先看下java doc中对parallelStream的定义。

A sequence of elements supporting sequential and parallel aggregate operations.

Stream pipelines may execute either sequentially or in parallel. This execution mode is a property of the stream. Streams are created with an initial choice of sequential or parallel execution. (For example, Collection.stream() creates a sequential stream, and Collection.parallelStream() creates a parallel one.) This choice of execution mode may be modified by the BaseStream.sequential() or BaseStream.parallel() methods, and may be queried with the BaseStream.isParallel() method.

既然可以并行的执行,废话不多说,先看一个例子。

class Person {

int id;

String name;

String sex;

float height;

public Person(int id, String name, String sex, float height) {

this.id = id;

this.name = name;

this.sex = sex;

this.height = height;

}

}

/* 构造数据 @return*/

public List constructPersons() {

List persons = new ArrayList();

for (int i = 0; i < 5; i++) {

Person p = new Person(i, “name” + i, “sex” + i, i);

persons.add(p);

}

return persons;

}

/* for @param persons*/

public void doFor(List persons) {

long start = System.currentTimeMillis();

for (Person p : persons) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

System.out.println(p.name);

}

long end = System.currentTimeMillis();

System.out.println(“doFor cost:” + (end – start));

}

/* 顺序流 @param persons*/

public void doStream(List persons) {

long start = System.currentTimeMillis();

persons.stream().forEach(x -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

System.out.println(x.name);

});

long end = System.currentTimeMillis();

System.out.println(“doStream cost:” + (end – start));

}

/* 并行流 @param persons*/

public void doParallelStream(List persons) {

long start = System.currentTimeMillis();

persons.parallelStream().forEach(x -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

System.out.println(x.name);

});

long end = System.currentTimeMillis();

System.out.println(“doParallelStream cost:” + (end – start));

}

执行结果:

name0

name1

name2

name3

name4

doFor cost:5021

name0

name1

name2

name3

name4

doStream cost:5076

name4

name0

name2

name3

name1

doParallelStream cost:1010

代码上 stream 和 parallelStream 语法差异较小,从执行结果来看,stream顺序输出,而parallelStream 无序输出;parallelStream 执行耗时是 stream 的五分之一。

可以看到在当前测试场景下,parallelStream 获得的相对较好的执行性能,那parallelStream背后到底是什么呢?

要深入了解parallelStream,首先要弄明白ForkJoin框架和ForkJoinPool。ForkJoin框架是java7中提供的并行执行框架,他的策略是分而治之。说白了,就是把一个大的任务切分成很多小的子任务,子任务执行完毕后,再把结果合并起来。

顺便说下ForkJoin框架和ThreadPoolExecutor的区别,ForkJoin框架可以使用数量有限的线程数,执行大量任务,并且这些任务之间是有父子依赖的,必须是子任务执行完成后,父任务才能执行。ThreadPoolExecutor 显然是无法支持这种场景的。而ForkJoin框架,可以让其中的线程创建新的任务,并挂起当前的任务,任务以及子任务会保留在一个内部队列中,此时线程就能够从队列中选择任务顺序执行。

Java 8为ForkJoinPool添加了一个通用线程池,这个线程池用来处理那些没有被显式提交到任何线程池的任务。它是ForkJoinPool类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的处理器数量。当调用Arrays类上添加的新方法时,自动并行化就会发生。比如用来排序一个数组的并行快速排序,用来对一个数组中的元素进行并行遍历。自动并行化也被运用在Java 8新添加的Stream API中。

上面的代码中,forEach方法会为每个元素的操作创建一个任务,该任务会被前文中提到的ForkJoinPool中的通用线程池处理。以上的并行计算逻辑当然也可以使用ThreadPoolExecutor完成,但是就代码的可读性和代码量而言,使用ForkJoinPool明显更胜一筹。

默认线程池的数量就是处理器的数量,特殊场景下可以使用系统属性:-Djava.util.concurrent.ForkJoinPool.common.parallelism={N} 调整。

对上面例子做下调整,sleep时间变为2ms,

Thread.sleep(2);

执行结果如下:

doFor cost:12

=======================

doParallelStream cost:62

=======================

doStream cost:13

doParallelStream耗时最多,可见并不是并行执行就是性能最好的,要根据具体的应用场景测试分析。这个例子中,每个子任务执行时间较短,而线程切换消耗了大量时间。

说到了并发,不得不提线程安全。先看一个例子:

public void doThreadUnSafe() {

List listFor = new ArrayList<>();

List listParallel = new ArrayList<>();

IntStream.range(0, 1000).forEach(listFor::add);

IntStream.range(0, 1000).parallel().forEach(listParallel::add);

System.out.println(“listFor size :” + listFor.size());

System.out.println(“listParallel size :” + listParallel.size());

}

输出结果:

listFor size :1000

listParallel size :949

显而易见,stream.parallel.forEach()中执行的操作并非线程安全。如果需要线程安全,可以把集合转换为同步集合,即:Collections.synchronizedList(new ArrayList<>())。

总结下来如下:使用parallelStream可以简洁高效的写出并发代码。

parallelStream并行执行是无序的。

parallelStream提供了更简单的并发执行的实现,但并不意味着更高的性能,它是使用要根据具体的应用场景。如果cpu资源紧张parallelStream不会带来性能提升;如果存在频繁的线程切换反而会降低性能。

任务之间最好是状态无关的,因为parallelStream默认是非线程安全的,可能带来结果的不确定性。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220040.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午9:23
下一篇 2026年3月17日 下午9:23


相关推荐

  • mysql主从复制读写分离-Altas

    mysql主从复制读写分离-Altasmysql 主从复制读写分离本文读写分离使用的软件是 Altas altas 是奇虎 360 公司开发的开源数据库代理软件 它是基于 mysql proxy 开发而成的它集中地响应应用的请求 依据用户事先设置的规则 将 SQL 请求发送到特定的数据库上执行 基于此可以实现负载均衡 读写分离 高可用性等需求 mysql 读写分离原理 数据库层在高并发的情况下 i o 会产生瓶颈 而实际上用户

    2026年3月17日
    2
  • implementation和api的区别

    implementation和api的区别关于 implementati 和 api 的区别 一共分为两个方面 一 编译范围关于这一部分 参考官网 说的很清楚 implementati Gradle 会将依赖项添加到编译类路径 并将依赖项打包到构建输出 不过 当您的模块配置 implementati 依赖项时 会让 Gradle 了解您不希望该模块在编译时将该依赖项泄露给其他模块 也就是说 其他模块只有在运行时才能

    2026年3月17日
    2
  • Pycharm 2019使用设置,让你用起来更便捷!

    Pycharm 2019使用设置,让你用起来更便捷!1 创建 Python 工程的的时候勾选下面的 第一个创建一个虚拟环境 不会包含自己安装的第三方包 2 当代码有 tab 缩进的时候 会用 显示 tab 键 3 设置 Python 新建文件默认信息 4 字体大小设置 5 Python2 Python3 解析器的切换 有时候因为建立工程时选择的 Python 版本不一致 导致编译时提醒选择编译器 可以在这里统一设置工程的编译器版本

    2026年3月27日
    2
  • toast弹窗的用法_vue弹出提示框

    toast弹窗的用法_vue弹出提示框效果图代码<!DOCTYPEhtml><htmllang=”en”> <head> <title>弹窗</title> <metacharset=”UTF-8″> <metaname=”viewport”content=”width=device-width,initial-sc…

    2026年3月11日
    6
  • 对java老师的评价及建议_对老师的评价和建议

    对java老师的评价及建议_对老师的评价和建议篇一:学生对教师评语学生对教师的评价()老师授课的方式非常适合我们,他根据本课程知识结构的特点,重点突出,层次分明。理论和实际相结合,通过例题使知识更条理化。但授课速度有点快,来不及记录。()老师授课有条理,有重点,对同学既热情又严格,是各位老师学习的榜样。()老师上课有时非常幽默,有时非常严格,不过还是非常有教授风度的,不妨自己来听听嘛!大家很崇拜他哦!=()老师治学严谨,要求严格,能深入了解学…

    2022年7月7日
    22
  • C++中__cplusplus宏介绍

    C++中__cplusplus宏介绍C 编程语言中 cplusplus 宏介绍 GCC 编译器预定义宏的查看

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号