前提:我已经阅读了this question和其他人,但我需要做一些澄清。
我理解Stream.forEach
方法在处理并行流时会产生差异(不仅仅是这个),这就解释了为什么会这样做
//1
Stream.of("one ","two ","three ","four ","five ","six ")
.parallel()
.forEachOrdered(item -> System.out.print(item));
打印
one two three four five six
但是当谈到中间操作时,当并行化流时,订单不再保证。所以这段代码
//2
Stream.of("one ","two ","three ","four ","five ","six ")
.parallel()
.peek(item -> System.out.print(item))
.forEachOrdered(item -> System.out.print(""));
将打印类似
的内容four six five one three two
说forEachOrdered
方法只影响其自身执行中元素的顺序是否正确?直觉上,我认为//1
示例与
//3
Stream.of("one ","two ","three ","four ","five ","six ")
.parallel()
.peek(item -> System.out.print("")) //or any other intermediate operation
.sequential()
.forEach(item -> System.out.print(item));
我的直觉是错的吗?我错过了整个机制的一些内容吗?
答案 0 :(得分:3)
#3代码在概念上与#1不同,因为parallel()
或sequential()
调用会影响整个流,而不仅仅影响后续操作。因此在#3情况下,整个过程将按顺序执行。实际上#3案例类似于实际可以更改并行/顺序模式时Stream API的早期设计。这被认为是不必要的复杂化(并且实际上添加了问题,例如,参见this discussion),因为通常您只需要更改模式以使终端操作有序(但不一定是顺序的)。因此添加了forEachOrdered()
并更改了parallel()/sequential()
语义以影响整个流(请参阅this changeset)。
基本上你是对的:在并行流中,没有中间操作的订单保证。如果需要按特定顺序执行它们,则必须使用顺序流。
答案 1 :(得分:3)
你是对的,因为forEachOrdered
的行动所做的保证只适用于该行动,而不适用于任何其他行为。但是假设这与.sequential().forEach(…)
相同是错误的。
sequential
会将整个流管道转换为顺序模式,因此,传递给forEach
的操作将由同一个线程执行,但也会执行前面的peek
操作。对于大多数中间操作,parallel
或sequential
的确切位置无关紧要,并指定两者都没有意义,因为只有最后一个是相关的。
此外,使用forEach
时仍然没有保证订购,即使它在当前实施中没有任何后果。这在“Does Stream.forEach respect the encounter order of sequential streams?”
documentation of Stream.forEachOrdered
州:
此操作一次处理一个元素,如果存在,则按顺序处理。对一个元素执行操作happens-before为后续元素执行操作,但对于任何给定元素,该操作可以在库选择的任何线程中执行。
因此,可以通过Thread.currentThread()
可感知但不同时运行的不同线程调用该操作。
此外,如果流具有遭遇订单,它将在此处重新构建。 This answer明确了遇到订单和处理订单的区别。