当前位置: 首页 > article >正文

Java 并行流(Parallel Stream)详解

并行流是Java 8引入的高效处理集合数据的工具,通过多线程加速计算。以下是其核心概念、使用方法及注意事项的详细指南:

1. 核心概念与原理

  • 并行处理机制:将数据分割为多个块,利用Fork/Join框架在多个线程上并行处理,最后合并结果。
  • 默认线程池:使用ForkJoinPool.commonPool(),线程数等于CPU核心数(可通过系统参数调整)。
  • 适用场景:大规模数据集、计算密集型任务(如数学运算、批量转换)。

2. 创建并行流的方式

  • 直接生成:通过集合的parallelStream()方法。
  • 转换顺序流:在现有流上调用parallel()
    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    
    // 方式1:直接生成并行流
    Stream<Integer> parallelStream1 = list.parallelStream();
    
    // 方式2:将顺序流转为并行
    Stream<Integer> parallelStream2 = list.stream().parallel();

3. 适用场景与性能优化

  • 推荐场景
    • 数据量大:如百万级元素的过滤、映射。
    • 计算复杂:如矩阵运算、图像处理。
    • 无状态操作:如mapfilterreduce(不依赖处理顺序或外部变量)。
  • 性能陷阱
    • 小数据集:并行化开销(线程调度、数据分割)可能抵消收益。
    • 低耗时操作:如简单加减法,并行可能更慢。

4. 注意事项与最佳实践

  1. 避免共享可变状态
    并行操作中修改共享变量会导致线程安全问题,应使用无状态操作或同步控制。

    // 错误示例:线程不安全的累加
    List<Integer> nums = Arrays.asList(1, 2, 3);
    int[] sum = {0};
    nums.parallelStream().forEach(n -> sum += n); // 结果可能错误
    
    // 正确做法:使用归约
    int safeSum = nums.parallelStream().reduce(0, Integer::sum);

  2. 谨慎使用有状态操作
    sorted()distinct()在并行流中可能更耗时,需合并线程结果。

    // 并行排序(可能比顺序流慢)
    List<Integer> sortedList = nums.parallelStream().sorted().toList();

  3. 数据源的可拆分性

    • 高效结构ArrayList、数组(支持快速随机访问,易于分割)。
    • 低效结构LinkedListTreeSet(拆分成本高)。
  4. 顺序敏感操作
    使用forEachOrdered保证顺序,但牺牲性能。

    // 按顺序输出(性能低于无序操作)
    list.parallelStream().forEachOrdered(System.out::println);

  5. 配置线程池

    • 默认线程数:Runtime.getRuntime().availableProcessors()
    • 修改全局线程数:
      # JVM启动参数
      -Djava.util.concurrent.ForkJoinPool.common.parallelism=8

5. 性能对比示例 

// 顺序流 vs 并行流(处理1000万数据)
List<Long> numbers = LongStream.rangeClosed(1, 10_000_000)
                               .boxed().collect(Collectors.toList());

// 顺序流耗时
long start = System.currentTimeMillis();
long seqSum = numbers.stream().mapToLong(n -> n * 2).sum();
System.out.println("顺序流耗时: " + (System.currentTimeMillis() - start) + "ms");

// 并行流耗时
start = System.currentTimeMillis();
long parSum = numbers.parallelStream().mapToLong(n -> n * 2).sum();
System.out.println("并行流耗时: " + (System.currentTimeMillis() - start) + "ms");

典型结果(8核CPU):

顺序流耗时: 120ms 并行流耗时: 35ms

6. 总结

  • 优势:简化多线程编程,提升大数据处理效率。
  • 局限:不适合小数据量、顺序敏感或低计算量任务。
  • 最佳实践
    • 优先处理大规模数据。
    • 避免操作共享变量。
    • 测试验证性能提升。
    • 使用forEach替代forEachOrdered除非必须保证顺序。

通过合理使用并行流,可在不增加复杂代码的情况下显著提升程序性能,但需结合场景权衡利弊。


http://www.kler.cn/a/570446.html

相关文章:

  • Spring学习笔记04:spring mvc和Spring Boot之间是什么关系?
  • 第151场双周赛:将数组按照奇偶性转化、可行数组的数目、移除所有数组元素的最小代价、全排列 Ⅳ
  • Linux系统安装Azure CLI完全指南
  • 【多线程-第三天-NSOperation的练习-tableView异步下载网络图片-沙盒缓存 Objective-C语言】
  • MySQL 8 C++ 源码解析:EXPLAIN 实现机制
  • 掌握 ElasticSearch 聚合查询:Aggregations 入门与实战
  • Vue.js 的双向数据绑定是如何实现的?
  • 快速创建基于Scala的flink开发项目
  • React 之 Redux 第二十八节 学习目标与规划大纲及概要讲述
  • chrome下载文件提示“贵组织屏蔽了该文件,因为它不符合安全政策” 安装chrome插件出现问题
  • 安装 cnpm 出现 Unsupported URL Type “npm:“: npm:string-width@^4.2.0
  • Hyper-V -docker-vmware 三者的关系
  • 零信任沙箱:为网络安全筑牢“隔离墙”
  • 6.人工智能与机器学习
  • 快瞳通用文档解析技术是怎样赋能下游各类大语言模型任务?
  • Lua | 每日一练 (4)
  • mapbox基础,使用geojson加载heatmap热力图层
  • 利用Java爬虫获取1688店铺所有商品信息:实战指南
  • 3. 前后端实现压缩包文件下载
  • 汽车电子电控软件开发中因复杂度提升导致的架构恶化问题