Java 8`Stream`可以并行,你甚至不需要它吗?
在我看来,显而易见的代码,当使用Java 8 Stream
,无论它们是“对象”流还是原始流(即IntStream
和朋友)都只是使用:
someStreamableResource.stream().whatever()
但是,相当多的“可流动资源”也有.parallelStream()
。
读取javadoc时不清楚的是.stream()
流是否始终是顺序的,以及.parallelStream()
流是否始终是并行的…
然后是Spliterator
,特别是它的.characteristics()
,其中之一就是它可以是CONCURRENT
,甚至是IMMUTABLE
。
我的直觉是,事实上,默认情况下, Stream
是否可以并行,或者根本不是并行,由其底层的Spliterator
引导…
我是在正确的轨道上吗? 我已经阅读并再次阅读了javadocs,但仍无法对这个问题做出明确答案……
首先,通过规范的镜头。 流是并行还是顺序是流的状态的一部分。 流创建方法应该指定它们是创建顺序流还是并行流(并且在JDK中大多数都是这样),但是它们不需要这样说。 如果你的流源没有说,请不要假设。 如果有人通过你的流,不要假设。
允许并行流自行决定回退到顺序流(因为顺序实现是并行实现,只是一个可能不完美的实现); 相反的情况并非如此。
现在,通过实施的镜头。 在Collections和其他JDK类中的流创建方法中,我们坚持“创建顺序流,除非用户明确要求并行性”的规则。 (然而,其他图书馆做出了不同的选择。如果他们有礼貌,他们会指明他们的行为。)
流并行性和Spliterator之间的关系只有一个方向。 Spliterator可以拒绝拆分 – 有效地拒绝任何并行性 – 但它不能要求客户端拆分它。 因此,不合作的Spliterator会破坏并行性,但不能确定它。
API在这个问题上没有太多话要说:
通过初始选择的顺序或并行执行来创建流。 (例如,Collection.stream()创建一个顺序流,Collection.parallelStream()创建一个并行流。)
关于某些中间操作可能不是线程安全的推理,您可能需要阅读包摘要 。 包概述讨论了中间操作,有状态与无状态,以及如何在某种程度上正确使用Stream
。
通常,不鼓励行为参数对流操作的副作用,因为它们通常会导致无意中违反无国籍要求以及其他线程安全危险。
行为参数是无状态中间操作的参数。
API无法做出任何假设
API可以做出任何愿望。 API的用户有责任满足这些假设。 但是,假设可能会限制可用性。 Stream
API 不鼓励创建非线程安全的无状态中间操作。 由于不鼓励而不是禁止,大多数Stream
将按顺序“默认”。
好吧,回答自己……
在仔细考虑之后(想想看,这样的事情只有在我实际提出问题之后才会发生),我实际上想出了一个原因……
中间操作可能不是线程安全的; 因此,API不能做任何假设,因此如果用户想要并行流,它必须明确地要求并确保流中使用的所有中间操作都是线程安全的。
然而, Collector
的情况有些误导; 由于Collector
无法预先知道它是否将被调用为并行或非并行流上的终端操作,因此合同清楚地表明“只是为了安全”,任何Collector
必须是线程安全的。
这里提到:“当你创建一个流时,它总是一个串行流,除非另有说明。” 在这里 :“此方法( parallelStream
)允许返回顺序流。”
CONCURRENT
和IMMUTABLE
没有(直接)与此相关。 它们指定是否可以修改基础集合而不会使spliterator无效或者它是否分别是不可变的。 spliterator的function几乎定义了parallelStream的行为是trySplit
。 并行流上的终端操作最终将调用trySplit
,并且无论该实现在一天结束时做什么,都会定义并行处理哪些部分(如果有的话)。
这个appart现在不受规范限制,但简短的答案是否定的 。 存在parallelStream()
和stream()
函数,但它只提供了访问流处理流的常见基本操作的并行或顺序实现的方法。 如果没有明确使用parallelStream()
或parallel()
调用,当前运行时不能假设您的操作是线程安全的,那么stream()
默认实现是具有顺序行为。