Java 8流中的无干扰精确含义

Java 8流中的无干扰精确含义,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,使用非并发数据结构源的流的非干扰要求是否意味着我们不能在流管道执行期间更改数据结构元素的状态(此外,我们不能更改源数据结构本身)?(问题1) 在“关于”一节中,在流包描述中,其表示: 对于大多数数据源,防止干扰意味着确保在流管道执行期间数据源根本不被修改 这篇文章没有提到修改元素的状态 例如,假设“shapes”是非线程安全的集合(例如ArrayList),下面的代码是否被认为存在干扰?(问题2) 这个例子取自a(至少可以这么说),所以它应该是正确的。 但是,如果我将stream()更改为par

使用非并发数据结构源的流的非干扰要求是否意味着我们不能在流管道执行期间更改数据结构元素的状态(此外,我们不能更改源数据结构本身)?(问题1)

在“关于”一节中,在流包描述中,其表示: 对于大多数数据源,防止干扰意味着确保在流管道执行期间数据源根本不被修改

这篇文章没有提到修改元素的状态

例如,假设“shapes”是非线程安全的集合(例如
ArrayList
),下面的代码是否被认为存在干扰?(问题2)

这个例子取自a(至少可以这么说),所以它应该是正确的。 但是,如果我将
stream()
更改为
parallelStream()
,它仍然安全且正确吗?(问题3)

另一方面,另一可靠消息来源Naftalin Maurice的“掌握Lambdas”表明,通过管道操作改变元素的状态(值)确实是干扰。从关于不干扰的章节(3.2.3)中:

但是流的规则禁止对流源进行任何修改,例如,通过任何线程而不仅仅是管道操作来更改元素的值


如果书中所说的是正确的,这是否意味着我们不能使用流API来修改元素的状态(使用
forEach
),而必须使用常规迭代器(或for each,或
Iterable.forEach
)来完成?(问题4)

修改存储在数据结构中的对象的状态不同于重新分配数据结构的元素

当另一个写“更改元素的值”时,它们可能意味着将新对象分配给现有
列表的索引

从您的:

最好避免在传递给流方法的lambda中产生任何副作用。虽然有些副作用(例如打印值的调试语句)通常是安全的,但从这些lambda访问可变状态可能会导致数据争用或令人惊讶的行为,因为lambda可能同时从多个线程执行,并且可能看不到按其自然相遇顺序排列的元素。非干扰不仅包括不干扰源,还包括不干扰其他lambda;当一个lambda修改可变状态,而另一个lambda读取它时,就会产生这种干扰

只要满足非干扰要求,我们就可以安全地执行并行操作,甚至在ArrayList等非线程安全源上也可以获得可预测的结果


这与并行性有关,与任何其他并发编程没有区别。修改状态可能会导致线程之间的可见性问题。

有一类更大的函数称为“具有副作用的函数”。JavaDoc语句是正确和完整的:这里的干扰意味着修改可变源。另一种情况是有状态表达式:依赖于应用程序状态或更改此状态的表达式。您可以在Oracle网站上阅读本教程

通常,您可以修改流元素本身,不应将其称为“干扰”。但是,如果流源多次生成相同的可变对象(例如,使用
Collections.nCopies(10,new MyMutableObject()).parallelStream()),请注意
。虽然可以确保同一个流元素不会被多个线程并发处理,但如果流生成同一元素两次,那么在
forEach
中修改它时,您肯定会遇到竞争条件

因此,虽然有状态表达式有时很难闻,如果有无状态的替代方案,则应谨慎使用并避免使用,但如果它们不干扰流源,则它们可能是正常的API文档中特别提到了这一点。在
forEach
文档中,仅要求无干扰

回到你的问题上来:

问题1:不,我们可以更改元素状态,它不称为干扰(尽管称为状态完整性)

问题2:不,它没有干扰,除非流源中有重复对象)

问题3:您可以在那里安全地使用
parallelStream()


问题4:不,在这种情况下可以使用流API。

换句话说,“干扰”存在于更高的层次上。当同时修改同一个元素时,修改流元素的操作可能会产生干扰,但当修改不同的对象时不会产生干扰。类似地,
ArrayList
本身不是线程安全的,并不排除在并发场景中正确使用
ArrayList
s的可能性。这一切都是关于,它是如何使用的…感谢塔吉尔为您提供清晰和详细的答案。在集合中维护可修改的元素是一项常见的任务,使用迭代器对集合执行的许多操作都需要更改元素的状态,因此这确实让我感到困扰。如果我们不能安全地做到这一点(至少在并行流的情况下),它会使流变得不那么有用。Tagir,请注意,有状态lambda在流包(以及您提到的链接)中定义为“其结果取决于流管道执行期间可能更改的任何状态的lambda”。是否继续,输入:)。它没有说任何关于改变状态本身的事情。因此,我发布的示例的lambda(s->s.setColor(红色))有副作用,但根据该定义-不是有状态的…感谢您的回答。具体来说,您在这里引用的内容使我对修改t元素的状态感到更加不确定
shapes.stream() 
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> s.setColor(RED));