Java 8 `Stream` 可以在您不要求的情况下并行吗?

Can a Java 8 `Stream` be parallel without you even asking for it?

如我所见,显而易见的代码,当使用 Java 8 Streams 时,无论它们是 "object" 流还是原始流(即 IntStream和朋友)将只是使用:

someStreamableResource.stream().whatever()

但是,很多"streamable resources"也有.parallelStream()

阅读 javadoc 时不清楚的是 .stream() 流是否总是顺序的,以及 .parallelStream() 流是否总是并行的...

然后是 Spliterator,特别是 .characteristics(), one of them being that it can be CONCURRENT, or even IMMUTABLE

我的直觉是,事实上,Stream 是否可以默认并行或完全并行,取决于其基础 Spliterator...

我走在正确的轨道上吗?我已经阅读并再次阅读了 javadocs,但仍然无法对这个问题给出明确的答案...

嗯,回答自己...

仔细想了想(算了,我真的问了才会有这种事),我居然想出了一个理由……

中间操作可能不是线程安全的;因此,API 无法做出任何假设,因此如果用户想要并行流,则必须明确要求并确保流中使用的所有中间操作都是线程安全的。

然而,Collectors 有点误导;由于 Collector 无法预先知道它是否会在并行或非并行流上作为终端操作被调用,因此合约明确规定 "just to be safe",任何 Collector 都必须是线程安全。

提到here:"When you create a stream, it is always a serial stream unless otherwise specified." 并且 here:"It is allowable for this method (parallelStream) to return a sequential stream."

CONCURRENTIMMUTABLE 与此没有(直接)相关。它们指定是否可以在不使拆分器无效的情况下修改基础集合,或者它是否是不可变的。 几乎定义了 parallelStream 行为的 spliterator 特性是 trySplit。并行流上的终端操作最终将调用 trySplit,无论该实现做什么,最终都将定义并行处理数据的哪些部分(如果有的话)。

此应用程序目前不受规范限制,但简短的回答是 。 存在 parallelStream()stream() 函数,但这只是为您提供了访问常见基本操作的并行或顺序实现以处理流的方法。 当前运行时不能假定您的操作在没有显式使用 parallelStream()parallel() 调用的情况下是线程安全的,那么 stream() 的默认实现是具有顺序行为。

API就此事无话可说:

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.)

关于某些中间操作可能不是线程安全的推理,您可能需要阅读 package summary。包摘要讨论了中间操作、有状态与无状态,以及如何在一定程度上正确使用 Stream

Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards.

行为参数是给予无状态中间操作的参数。

the API cannot make any assumptions

API 可以做出任何它希望的假设。 API 的用户有责任满足这些假设。但是,假设可能会限制可用性。 Stream API 不鼓励 创建非线程安全的无状态中间操作。由于不鼓励而不是禁止,因此大多数 Stream 将是连续的 "by default"。

首先,通过规范的镜头。流是并行的还是顺序的是流状态的一部分。流创建方法应指定它们是创建顺序流还是并行流(JDK 中的大多数方法都这样做),但并不要求它们如此说明。如果您的流源没有说明,请不要假设。如果有人向您传递信息流,请不要假设。

允许并行流自行决定回退到顺序(因为顺序实现 并行实现,只是一个可能不完美的实现);反之则不然。

现在,通过 实施 的镜头。在 Collections 和其他 JDK 类 中的流创建方法中,我们坚持 "create a sequential stream unless the user explicitly asks for parallelism" 的原则。 (然而,其他图书馆做出不同的选择。如果他们有礼貌,他们会指定他们的行为。)

流并行和Spliterator之间的关系只有一个方向。 Spliterator 可以拒绝拆分——有效地拒绝任何并行性——但它不能要求 客户端拆分它。因此,不合作的 Spliterator 可以破坏并行性,但不能决定它。