Java 中的并行流与顺序流

2025 年 1 月 6 日 | 阅读 4 分钟

在 Java 中,流 (stream) 是对象的集合,可以对数据源(如数组或集合)执行各种操作,并支持不同的方法。它最初包含在 Java 8 的 java.util.stream 包中。流支持许多聚合操作,例如 filter、map、limit、reduce、find 和 match,允许程序员以任何他们认为合适的方式修改原始数据。由于传递给流的操作不会改变其源,因此会根据对其执行的操作生成一个新流。更新后的数据是初始格式的修改副本。

根据其迷人的特性,Java 已经拥有流 API 很长时间了。它增强的性能和并行处理能力也使其非常受欢迎。鉴于现代时代几乎所有现代系统都有多个核心,因此并行流对于有效利用这些核心是必不可少的,但并行编程设计很困难。因此,根据需求,程序员完全可以控制是使用顺序流还是并行流。

Parallel vs Sequential Stream in Java

顺序流 (Sequential Stream)

顺序流是非并行流,它们使用单个线程处理管道。如果流操作没有被明确指定为并行,则该流被认为是顺序的。即使底层系统支持并行执行,顺序流也不会利用多核处理器,因为它的对象在同一处理系统上的单个流中进行管道化。顺序流一次执行一个操作。Java 中的顺序流由 stream() 函数返回。

示例

在此示例中,print() 函数与 list.stream() 结合使用,在单个通用线程上逐个操作。原始程序的输出仅仅是因为流是顺序的,就显示了列表的内容,内容按有序序列排列。

实施

文件名: SequentialStreamExample.java

输出

Hello Welcome to WORLD!

Java 中的并行流

虽然并行计算可能不会在整个程序中使用,但它仍然是 Java 的一个非常有用的特性。并行流通过使用多核 CPU 来提高性能。最终结果显示为每个独立核心结果的组合。这可以通过使用并行流来实现,并行流将我们的代码分成许多流,这些流可以在不同的系统核心上并发处理。一个程序不一定需要完全并行化;然而,处理流的部分应该并行化。它们很复杂且容易出错,就像任何其他并行编程一样,但我们无法控制它们的执行顺序。这可能导致结果不稳定、不可预测。

Java 流库提供了两种快速且行为一致的方法来实现这一点。

  • 使用 Collection 接口的 parallelStream() 函数是获取并行流的最简单方法之一。
  • 通过对顺序流调用 BaseStream 接口的 parallel() 方法是另一种方法。

为了确保并行流提供的结果与通过顺序流获得的结果相同,它们需要是无状态的、非干扰的并且是关联的。

示例

正如我们所见,顺序与列表不一致。通过使用 parallelStream(),并行操作使用了多个线程。如果我们多次执行此代码,输出顺序也会发生变化,这一点也很明显。但是,由于这种并行流提高了效率,因此建议在顺序无关紧要的情况下使用此技术。

实施

文件名: ParallelStreamExample.java

输出

RLHello Welcome to OD!W

注意:如果我们希望对并行流中的每个元素进行排序,可以使用 forEachOrdered() 函数代替 forEach() 方法。观察以下程序。

实施

文件名: ParallelStreamExample2.java

输出

Hello Welcome to WORLD!

Java 中顺序流和并行流的区别

顺序流并行流
在单个计算机核心上运行。利用计算机的多个核心。
性能较差。性能非常好。
有顺序。不关心顺序。
一次只有一个迭代,与 for-loop 一致。利用多个可用核心同时运行多个迭代。
每个迭代都等待当前正在运行的迭代完成。仅在某个时刻没有空闲或可用核心时才等待。
准确性较低,可靠性较高。可靠性较低,容易出错。
平台独立。平台相关。

下一个主题Java泛型限制