《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一1.14 并行流

简介: 本节书摘来华章计算机《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一书中的第1章 ,第1.14节,[美] 凯S.霍斯特曼(Cay S. Horstmann) 著陈昊鹏 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.14 并行流

流使得并行处理块操作变得很容易。这个过程几乎是自动的,但是需要遵守一些规则。首先,必须有一个并行流。可以用Collection.parallelStream()方法从任何集合中获取一个并行流:
image

只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被并行化。
当流操作并行运行时,其目标是要让其返回结果与顺序执行时返回的结果相同。重要的是,这些操作可以以任意顺序执行。
下面的示例是一项你无法完成的任务。假设你想要对字符串流中的所有短单词计数:
image

这是一种非常非常糟糕的代码。传递给forEach的函数会在多个并发线程中运行,每个都会更新共享的数组。正如我们在卷Ⅰ第14章中所解释的,这是一种经典的竞争情况。如果多次运行这个程序,你很可能就会发现每次运行都会产生不同的计数值,而且每个都是
错的。
你的职责是要确保传递给并行流操作的任何函数都可以安全地并行执行,达到这个目的的最佳方式是远离易变状态。在本例中,如果用长度将字符串群组,然后分别对它们进行计数,那么就可以安全地并行化这项计算。
image

警告:传递给并行流操作的函数不应该被堵塞。并行流使用fork-join池来操作流的各个部分。如果多个流操作被阻塞,那么池可能就无法做任何事情了。
默认情况下,从有序集合(数组和列表)、范围、生成器和迭代产生的流,或者通过调用Stream.sorted产生的流,都是有序的。它们的结果是按照原来元素的顺序累积的,因此是完全可预知的。如果运行相同的操作两次,将会得到完全相同的结果。
排序并不排斥高效的并行处理。例如,当计算stream.map(fun)时,流可以被划分为n的部分,它们会被并行地处理。然后,结果将会按照顺序重新组装起来。
当放弃排序需求时,有些操作可以被更有效地并行化。通过在流上调用unordered方法,就可以明确表示我们对排序不感兴趣。Stream.distinct就是从这种方式中获益的一种操作。在有序的流中,distinct会保留所有相同元素中的第一个,这对并行化是一种阻碍,因为处理每个部分的线程在其之前的所有部分都被处理完之前,并不知道应该丢弃哪些元素。如果可以接受保留唯一元素中任意一个的做法,那么所有部分就可以并行地处理(使用共享的集来跟踪重复元素)。
还可以通过放弃排序要求来提高limit方法的速度。如果只想从流中取出任意n个元素,而并不在意到底要获取哪些,那么可以调用:
image

正如1.9节所讨论的,合并映射表的代价很高昂。正是因为这个原因,Collectors.groupByConcurrent方法使用了共享的并发映射表。为了从并行化中获益,映射表中值的顺序不会与流中的顺序相同。
image

警告:不要修改在执行某项流操作后会将元素返回到流中的集合(即使这种修改是线程安全的)。记住,流并不会收集它们的数据,数据总是在单独的集合中。如果修改了这样的集合,那么流操作的结果就是未定义的。JDK文档对这项需求并未做出任何约束,并且对顺序流和并行流都采用了这种处理方式。
更准确地讲,因为中间的流操作都是惰性的,所以直到执行终结操作时才对集合进行修改仍旧是可行的。例如,下面的操作尽管并不推荐,但是仍旧可以工作:
AI 代码解读

image

为了让并行流正常工作,需要满足大量的条件:

  • 数据应该在内存中。必须等到数据到达是非常低效的。
  • 流应该可以被高效地分成若干个子部分。由数组或平衡二叉树支撑的流都可以工作得很好,但是Stream.iterate返回的结果不行。
  • 流操作的工作量应该具有较大的规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义。
  • 流操作不应该被阻塞。
    换句话说,不要将所有的流都转换为并行流。只有在对已经位于内存中的数据执行大量计算操作时,才应该使用并行流。

程序清单1-8中的示例程序展示了如何操作并行流。
程序清单1-8 parallel/ParallelStreams.java
image
image
image

image

目录
打赏
0
0
0
0
1408
分享
相关文章
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
151 2
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
127 2
Java网络编程进阶:Socket通信的高级特性与应用
【6月更文挑战第21天】Java Socket通信是分布式应用的基础,涉及高级特性如多路复用(Selector)和零拷贝,提升效率与响应速度。结合NIO和AIO,适用于高并发场景如游戏服务器和实时数据分析。示例展示了基于NIO的多路复用服务器实现。随着技术发展,WebSockets、HTTP/2、QUIC等新协议正变革网络通信,掌握Socket高级特性为应对未来挑战准备。
77 1
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
6月前
|
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
210 3
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
50 2
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
124 3
大数据-73 Kafka 高级特性 稳定性-事务 相关配置 事务操作Java 幂等性 仅一次发送
大数据-73 Kafka 高级特性 稳定性-事务 相关配置 事务操作Java 幂等性 仅一次发送
68 2
Java中的高效并行计算与多线程编程技术
Java中的高效并行计算与多线程编程技术
|
10月前
|
Java并行流问题之parallelStream的使用方式
Java并行流问题之parallelStream的使用方式
163 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等