前提:我已经阅读了这个问题和其他问题,但我需要一些澄清。
我知道Stream.forEach方法在处理并行流时会产生差异(不仅仅是这个),这就解释了为什么会这样
| 12
 3
 4
 
 | //1
Stream.of("one" ,"two" ,"three" ,"four" ,"five" ,"six") 
.parallel() 
.forEachOrdered( item -> System .out .print( item)); | 
版画
| 1
 | one two three four five six | 
但是当涉及到中间操作时,当并行化流时,不再保证顺序。 所以这段代码
| 12
 3
 4
 5
 
 | //2
Stream.of("one" ,"two" ,"three" ,"four" ,"five" ,"six") 
.parallel() 
.peek( item -> System .out .print( item)) 
.forEachOrdered( item -> System .out .print("")); | 
会印出类似的东西
| 1
 | four six five one three two | 
说forEachOrdered方法只影响其自身执行中元素的顺序是否正确? 直觉上,我认为//1的例子完全相同
| 12
 3
 4
 5
 6
 
 | //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)); | 
我的直觉是错的吗? 我错过了整个机制的一些内容吗?
		
		
- 
不,你不能将一个op转为并行,而另一个转为顺序。 传递给forEachOrdered的操作仍然可能由不同的线程调用,但它们将适当地同步。
- 
@Holger,我不明白......
- 
在一个点上同步的多个线程仍然与单线程执行不同。
- 
@Holger我想我理解你的意思。 但是对于我的具体情况,我只关注元素顺序而不考虑线程重叠问题,//1 //3在概念上是等价的吗?
 
	 
你是对的,因为forEachOrdered的行动所做的保证只适用于那个动作而没有其他任何内容。但是假设这与.sequential().forEach(…)相同是错误的。
sequential会将整个流管道转换为顺序模式,因此,传递给forEach的操作将由同一个线程执行,但也会执行前面的peek操作。对于大多数中间操作,parallel或sequential的确切位置是无关紧要的,并指定两者都没有意义,因为只有最后一个是相关的。
此外,即使在当前实现中没有任何后果,使用forEach时仍然无法保证订购。这在"Stream.forEach是否遵循顺序流的遭遇顺序?"中讨论。
Stream.forEachOrdered的文档说明:
This operation processes the elements one at a time, in encounter order if one exists. Performing the action for one element happens-before performing the action for subsequent elements, but for any given element, the action may be performed in whatever thread the library chooses.
因此,动作可能会被不同的线程调用,如Thread.currentThread()可感知但不会同时运行。
此外,如果流具有遭遇顺序,则它将在此处重构。这个答案揭示了遭遇顺序和处理顺序的区别。
		
		
- 
"顺序会将整个流管道变为顺序模式",这是我完全遗漏的东西!
 
	 
#3代码在概念上不等同于#1,因为parallel()或sequential()调用影响整个流,而不仅仅影响后续操作。因此在#3情况下,整个过程将按顺序执行。实际上#3案例类似于实际可以更改并行/顺序模式时Stream API的早期设计。这被认为是不必要的复杂化(并且实际上添加了问题,例如,参见此讨论),因为通常您只需要更改模式以使终端操作有序(但不一定是顺序的)。因此添加了forEachOrdered()并更改了parallel()/sequential()语义以影响整个流(请参阅此变更集)。
基本上你是对的:在并行流中,没有中间操作的订单保证。如果需要按特定顺序执行它们,则必须使用顺序流。