【问题标题】:Java Stream: difference between forEach and forEachOrderedJava Stream:forEach 和 forEachOrdered 之间的区别
【发布时间】:2016-03-28 20:26:39
【问题描述】:

前提:我已经阅读了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));

我的直觉错了吗?我是否遗漏了整个机制的某些内容?

【问题讨论】:

  • 不,你不能把一个操作变成并行,另一个变成顺序。传递给forEachOrdered 的操作仍然可能被不同的线程调用,但它们会适当地同步。
  • @Holger,我不明白...
  • 多线程,在一点同步,仍然与单线程执行不同。
  • @Holger 我想我明白你的意思。但是对于我的具体情况,我只对元素顺序感兴趣而不考虑线程重叠问题,//1//3 在概念上是否等效?

标签: java foreach java-8 java-stream


【解决方案1】:

您是对的,为forEachOrdered 的操作所做的保证仅适用于该操作,而不适用于其他任何操作。但假设这与.sequential().forEach(…) 相同是错误的。

sequential 会将整个流管道转换为顺序模式,因此传递给forEach 的动作将由同一线程执行,而且前面的peek 的动作也会执行。对于大多数中间操作,parallelsequential 的确切位置是无关紧要的,同时指定两者是没有意义的,因为只有最后一个才是相关的。

此外,使用forEach 时仍然没有对排序做出任何保证,即使它在当前实现中没有任何后果。这在“Does Stream.forEach respect the encounter order of sequential streams?”

中进行了讨论

documentation of Stream.forEachOrdered 声明:

此操作一次处理一个元素,如果存在则按遇到的顺序。为一个元素执行操作 happens-before 为后续元素执行操作,但对于任何给定元素,可以在库选择的任何线程中执行操作。

因此动作可能会被不同的线程调用,正如Thread.currentThread() 所感知的那样,但不会同时运行。 此外,如果流有遭遇订单,它将在此位置重组。 This answer 揭示了遭遇顺序处理顺序的区别。

【讨论】:

  • “sequential 将把整个流管道变成顺序模式”,这是我完全错过的!
【解决方案2】:

#3 代码在概念上并不等同于#1,因为parallel()sequential() 调用会影响整个流,而不仅仅是后续操作。所以在#3 的情况下,整个过程将按顺序执行。实际上,#3 案例类似于 Stream API 的早期设计,您实际上可以更改并行/顺序模式。这被认为是不必要的复杂性(实际上增加了问题,例如,参见this discussion),因为通常您只需要更改模式即可使终端操作有序(但不一定是顺序的)。因此添加了forEachOrdered() 并更改了parallel()/sequential() 语义以影响整个流(请参阅this changeset)。

基本上你是对的:在并行流中,中间操作没有顺序保证。如果您需要按特定顺序执行它们,则必须使用顺序流。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 2016-06-29
    • 2012-09-03
    • 2017-12-23
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多