Java 多线程之并行流(parallelStream)

文章目录

    • 一、概述
    • 二、使用方法
      • 2.1 parallelStream 使用
      • 2.2 Stream 方法介绍
      • 2.3 简单示例
    • 三、应用示例
      • 3.1 示例介绍
      • 3.2 简单任务测试结果
      • 3.3 复杂任务测试结果
      • 3.4 结论
    • 四、完整示例

一、概述

  • 并行流是Java中Stream API的一部分,用于在多核处理器上并行执行流操作。在Java 8及更高版本中引入的Stream API提供了一种新的处理集合的方式,使得开发者能够更轻松地进行数据处理和操作。

  • 在使用Stream API时,可以将集合转换为流,然后进行各种操作,例如过滤、映射、排序等。在这个过程中,流可以是串行流(Sequential Stream)或并行流(Parallel Stream)。

  • 并行流通过使用多线程并行处理数据,充分利用多核处理器的优势,从而在某些情况下提高处理速度。使用并行流非常简单,只需在流上调用 parallel() 方法即可将其转换为并行流。其实本质上是使用线程池 FrorkJoinPool。

  • 本文后面将通过测试来说明实际应用场景应该如何选择并行流。但是可能我的测试有局限性,如果有任何错误欢迎大家指正。

二、使用方法

2.1 parallelStream 使用

  • 使用方法就是调用集合的 parallelStream() 方法转为并行流,然后再使用 stream 执行后续计算就是并行计算(使用线程池)。

    List<String> list = new ArrayList<>();
    Stream<String> stream =  List.parallelStream()
    

2.2 Stream 方法介绍

  • 使用 parallelStream 对集合进行并行操作,类似于普通的串行流操作,主要方法如下:

  • forEach 方法,对流中的每个元素执行指定的操作,可以并行执行。如下调用 System.out.println 输出显示 list 每个元素。

    List<String> list = Arrays.asList("apple", "banana", "orange");
    list.parallelStream().forEach(System.out::println); // System.out::println 是 System.out.println 的 Lambda 写法
    
  • filter 方法,使用给定的条件过滤流中的元素。如下筛选出偶数并输出显示。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    numbers.parallelStream().filter(n -> n % 2 == 0).forEach(System.out::println);// n -> n % 2 == 0 是 Lambda 写法,相当于一个函数 bool f(int n){ return n % 2 == 0; }
    
  • map 方法,将流中的每个元素映射到另一个值。如下把每个元素转成大写。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    words.parallelStream().map(String::toUpperCase).forEach(System.out::println);
    
  • reduce 方法,使用给定的累积函数进行归约操作。如下,进行求和操作

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.parallelStream().reduce(0, Integer::sum);
    System.out.println("求合结果: " + sum);
    
  • collect 方法,将流中的元素收集到一个集合中。如下将并行计算结果存储到 uppercaseWords 变量中。

    List<String> words = Arrays.asList("apple", "banana", "orange");
    List<String> uppercaseWords = words.parallelStream().map(String::toUpperCase).collect(Collectors.toList());
    
  • anyMatch 和 allMatch 方法

    • anyMatch 用于判断流中是否有任意一个元素匹配给定条件
    • allMatch 用于判断流中的所有元素是否都匹配给定条件。
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    boolean anyEven = numbers.parallelStream().anyMatch(n -> n % 2 == 0); // 只要有一个是偶数就是为真
    boolean allEven = numbers.parallelStream().allMatch(n -> n % 2 == 0); // 必须全部是偶数才为真
    

    这些方法在串行流中可以使用用,在并行流中可以充分发挥多核处理器的优势。

    请注意,使用并行流时需要确保操作是无状态的,以避免并发问题。在实际应用中,可以根据任务的性质和数据规模选择适当的流类型。

2.3 简单示例

  • 下面是一个简单的示例,演示集合并行流的使用

        private static void  test1()  {List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "peach");// 串行流操作(单线程,效率低)list.stream().map(String::toUpperCase).forEach(System.out::println);System.out.println("---------");// 并行流操作(多线程,效率高)list.parallelStream().map(String::toUpperCase).forEach(System.out::println);}
    
  • 在这个示例中,关键是看 list.parallelStream(),使用他创建了一个并行流。然后使用 map 方法将元素转换为大写,然后通过 forEach 方法打印出来。

    • String::toUpperCase 是 Lambda 写法(同 “xxxx”.toUpperCase ),结合 map ,相当于对集合中的每一个元素进行一次 toUpperCase (转大写)
    • System.out::println 也是 Lambda 写法(同 System.out.println(“xxxx”) ),结合 forEach,相当于对集合中的每个元素进行打印显示。
  • 并不是所有情况下都应该使用并行流,因为在某些场景下,多线程的开销可能会超过并行执行所带来的性能提升。选择使用串行流还是并行流应该根据实际情况和性能测试来进行权衡。在某些情况下,使用并行流可以有效提高处理速度,但在其他情况下,串行流可能更为合适。

三、应用示例

3.1 示例介绍

  • 在接下的示例中我,准备随机生成 1 千万个随机数,然后第使用 for 循环、串行流、并行流这三种方法找出数字为 250 的数。以此来检查运行效率。

  • 生成数据代码

    private static long DATA_COUNT  = 1000_0000;List<Double> list = new ArrayList<>();
    for (int i = 0; i < DATA_COUNT; i++) {list.add(Math.random() * 1000 % 500);
    }
    
  • for 循环查找代码

    List<Double> result = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {//sleep(1);Double val = list.get(i);if(val > 250 && val < 251){result.add(val);}
    }
    
  • 串行流筛选代码

    Stream<Double> result = list.stream().filter((val) -> {//sleep(1);return val > 250 && val < 251;
    });
    
  • 并行流筛选代码

    Stream<Double> result = list.parallelStream().filter((val) -> {//sleep(1);return val > 250 && val < 251;
    });
    

3.2 简单任务测试结果

  • 数据量1万时测试结果(DATA_COUNT = 1_0000)

    for 循环单线程查找,共用时(ms):3
    串行流单线程查找,共用时(ms):4
    并行流多线程查找,共用时(ms):4

  • 数据量100万时测试结果(DATA_COUNT = 100_0000)

    for 循环单线程查找,共用时(ms):40
    串行流单线程查找,共用时(ms):28
    并行流多线程查找,共用时(ms):45

  • 数据量1000万时测试结果(DATA_COUNT = 1000_0000)

    for 循环单线程查找,共用时(ms):181
    串行流单线程查找,共用时(ms):145
    并行流多线程查找,共用时(ms):161

  • 通过以上测试发现,在处理简单任务计算时,这三种方式并与多大差别。但是简单任务更推荐使用串行流

  • 通过修改以下数字修改数据量

    private static long DATA_COUNT  = 1000_0000;
    

3.3 复杂任务测试结果

  • 注意上面的代码,每个计算中都有一个 sleep(1)方法,现在我们使用这个方法来模拟复杂任务处理(如访问数据库、读写文件、访问网络等)进行测试。

  • 数据量1千时测试结果(DATA_COUNT = 1000)

    for 循环单线程查找,共用时(ms):1608
    串行流单线程查找,共用时(ms):1626
    并行流多线程查找,共用时(ms):165

  • 数据量1万时测试结果(DATA_COUNT = 1_0000)

    for 循环单线程查找,共用时(ms):16302
    串行流单线程查找,共用时(ms):16965
    并行流多线程查找,共用时(ms):2126

  • 通过以上测试发现,在处理复杂任务计算时,并行流表现十分优越。所以复杂任务推荐使用并行流

3.4 结论

  • 简单任务推荐使用串行流
  • 复杂任务推荐使用并行流
  • 简单任务你可以理解为你要执行的任务占用CPU时间小于1ms,反之复杂任务就是大于1ms。

四、完整示例

  • 以下完整测试代码

    package top.yiqifu.study.p004_thread;import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.List;
    import java.util.concurrent.*;
    import java.util.stream.Stream;// 包装类
    public class Test131_ParallelStream {private static long DATA_COUNT  = 10000;public static void main(String[] args) {test1();List<Double> list = new ArrayList<>();for (int i = 0; i < DATA_COUNT; i++) {list.add(Math.random() * 1000 % 500);}long ctime = test2(list);long stime = test3(list);long mtime = test4(list);System.out.println("for 循环单线程查找,共用时(ms):"+(ctime));System.out.println("串行流单线程查找,共用时(ms):"+(stime));System.out.println("并行流多线程查找,共用时(ms):"+(mtime));}private static void  test1()  {List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "peach");// 串行流操作(单线程)list.stream().map(String::toUpperCase).forEach(System.out::println);System.out.println("---------");// 并行流操作(多线程)list.parallelStream().map(String::toUpperCase).forEach(System.out::println);}private static long  test2(List<Double> list)  {long startTime = System.currentTimeMillis();List<Double> result = new ArrayList<>();for (int i = 0; i < list.size(); i++) {sleep(1);Double val = list.get(i);if(val > 250 && val < 251){result.add(val);}}for(Double val : result){System.out.println(val);};long endTime = System.currentTimeMillis();return endTime - startTime;}private static long  test3(List<Double> list)  {long startTime = System.currentTimeMillis();Stream<Double> result = list.stream().filter((val) -> {sleep(1);return val > 250 && val < 251;});result.forEach(val->{System.out.println(val);});long endTime = System.currentTimeMillis();return endTime - startTime;}private static long  test4(List<Double> list)  {long startTime = System.currentTimeMillis();Stream<Double> result = list.parallelStream().filter((val) -> {sleep(1);return val > 250 && val < 251;});result.forEach(val->{System.out.println(val);});long endTime = System.currentTimeMillis();return endTime - startTime;}private static void sleep(long ms){try {Thread.sleep(ms);} catch (InterruptedException e) {e.printStackTrace();}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/239173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

综述 2022-Egyptian Informatics Journal:电子健康记录的安全和隐私

Keshta, Ismail, and Ammar Odeh. "Security and privacy of electronic health records: Concerns and challenges." Egyptian Informatics Journal 22.2 (2021): 177-183. https://doi.org/10.1016/j.eij.2020.07.003 被引次数&#xff1a;207 IF 5.2 / JCR Q2

CSS3多列分页属性

CSS3多列 Firefox浏览器支持该属性的形式是-moz-column-count&#xff0c;而基于Webkit的浏览器&#xff0c;例如Safari和Chrome&#xff0c;支持该属性的形式是-webkit-column-count column-count&#xff1a;该属性定义多列文本流中的栏数 语法&#xff1a;column-count:int…

YACS(上海计算机学会竞赛平台)三星级挑战——两数之和

题目描述 给定 n 个整数 a[1]​,a[2]​,⋯,a[n]​&#xff0c;并且保证 a[1​]≤a[2​]≤⋯≤a[n]​ 再给定一个目标值 t&#xff0c;请判断能否找到 a[i]​ 与 a[j]​&#xff0c;ai​aj​t 且 i≠j。 输入格式 第一行&#xff1a;单个整数n&#xff1b; 第二行&#xf…

智能优化算法应用:基于卷尾猴算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于卷尾猴算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于卷尾猴算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.卷尾猴算法4.实验参数设定5.算法结果6.参考文…

C++ 函数重载、操作符重载

依然是温故而知新&#xff0c;不过现在更多的是以此为乐的心态啦。本篇通过代码实例&#xff0c;展示c函数重载相关知识&#xff0c;包括构造函数的重载、操作符重载等。 在构造函数重载中&#xff0c;给大家带来点稍微提升的用法&#xff0c; 看了不吃亏&#xff0c;看了不上当…

如何快速实现地源热泵远程监控

地源热泵远程监控解决方案 一、项目背景 山东省潍坊市盛世花园小区地源热泵项目是一个先进的供暖与制冷系统&#xff0c;旨在为整个小区提供高效且节能的温控服务。该系统主要由地下管道网络、地源热泵单元以及室内分配系统组成。 针对现有的地源热泵系统的管理和监控问题&a…

110基于matlab的混合方法组合的极限学习机和稀疏表示进行分类

基于matlab的混合方法组合的极限学习机和稀疏表示进行分类。通过将极限学习机&#xff08;ELM&#xff09;和稀疏表示&#xff08;SRC&#xff09;结合到统一框架中&#xff0c;混合分类器具有快速测试&#xff08;ELM的优点&#xff09;的优点&#xff0c;且显示出显着的分类精…

【NAM】《NAM:Normalization-based Attention Module》

NeurIPS-2021 workshop 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metrics5.2 Experiments 6 Conclusion&#xff08;own&#xff09; 1 Background and Motivation 注意力机制是近些年视觉领域…

栈与队列part01-算法

栈与队列 今日任务&#xff1a; ● 理论基础 ● 232.用栈实现队列 ● 225. 用队列实现栈 1.232.用栈实现队列 232. 用栈实现队列 class MyQueue {//使用两个栈实现先入先出队列//定义两个Stack集合//就已经拥有了这个集合的方法了push pop peek isEmpty等等//但是我们这里是…

WPF组合控件TreeView+DataGrid之DataGrid封装

&#xff08;关注博主后&#xff0c;在“粉丝专栏”&#xff0c;可免费阅读此文&#xff09; wpf的功能非常强大&#xff0c;很多控件都是原生的&#xff0c;但是要使用TreeViewDataGrid的组合&#xff0c;就需要我们自己去封装实现。 我们需要的效果如图所示&#x…

关于mysql的知识体系

关系型数据库&#xff08;二维表&#xff09; 常用的SQL命令分类 管理数据库使用SQL&#xff08;结构化查询语言&#xff09; DDL 数据定义语言 如&#xff1a;CREATE、ALTER、DROP DML 数据操作语言 如&#xff1a;INSERT、UPDATE、DELETE DCL 数据控制语言 如&#xff1a…

[python]python实现对jenkins 的任务触发

目录 关键词平台说明背景一、安装 python-jenkins 库二、code三、运行 Python 脚本四、注意事项 关键词 python、excel、DBC、jenkins 平台说明 项目Valuepython版本3.6 背景 用python实现对jenkins 的任务触发。 一、安装 python-jenkins 库 pip install python-jenkin…

论文解读:Informer-AAAI2021年最佳论文

论文背景 应用背景 训练的是历史数据&#xff0c;但预测的是未来的数据&#xff0c;但是历史数据和未来数据的分布不一定是一样的&#xff0c;所以时间序列应用于股票预测往往不太稳定 动作预测&#xff1a; 基于之前的视频中每一帧动作&#xff0c;预测下一帧这个人要做什么…

Ubuntu 常用命令之 echo 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 echo 是一个在 Ubuntu 系统下常用的命令&#xff0c;主要用于在终端输出字符串或者变量。 echo 的基本语法 echo [option] [string]echo 命令的参数包括 -n&#xff1a;不输出结尾的换行符。-e&#xff1a;启用反斜杠转义字符。…

超级逼真人脸生成,Stable Diffusion的3个关键技巧

大家好&#xff0c;你是否曾想过&#xff0c;为什么别人可以使用AI图像生成技术生成如此逼真的人脸&#xff0c;而自己的尝试却充满了错误和瑕疵&#xff0c;让人一眼看出是假的。尝试过调整提示和设置&#xff0c;但似乎仍无法与他人的质量相匹配。 本文将带大家了解使用Stab…

LeetCode-17 电话号码的字母组合

LeetCode-17 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;d…

医保dip质控系统如何实现医保控费?

医保DIP质控系统通过数据分析和监管手段实现医保控费的目标。下面是一些常见的实现方式&#xff1a; 医疗服务审核&#xff1a;系统对医疗机构提供的医疗服务进行审核&#xff0c;确保医疗行为符合规范和政策要求。例如&#xff0c;对门诊病历、住院病历等进行审核&#xff0c;…

硬件产品经理:硬件产品敏捷开发

目录 简介 敏捷 CSDN学院 作者简介 简介 之所以敏捷产品开发流程会越来越普遍。 主要得益于这个方法可以让企业使用更少的资源去开发出令客户满意的新产品。 敏捷开发强调的最重要的一点就是“快”。 也就是要求通过快速迭代来获取频繁的客户反馈。 这就特别适合应对市…

Python教你如何让代码摆脱死循环的困扰!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在编写Python代码时&#xff0c;无限循环是一个常见的问题&#xff0c;可能导致程序陷入死循环&#xff0c;使得代码无法正常执行。这篇博客将介绍一些方法&#xff0c;帮助大家防止和处理无限循环&#xff0c;确…

ansible变量的使用

本章主要介绍playbook中的变量 自定义变量使用变量文件字典变量列表变量facts变量内置变量变量的过滤器 为了能够写出更实用的playbook&#xff0c;需要在playbook中使用变量。下面来讲解playbook 中常见的变量。本章实验都在/home/lduan/demo2下操作&#xff0c;先把 demo2目…