首页 > 解决方案 > 规范是否保证对顺序 Java 流的操作必须保留在当前线程中?

问题描述

规范是否保证对顺序Java 流的所有操作都在当前线程中执行?(“forEach”和“forEachOrdered”除外)

我明确要求规范,而不是当前实现的功能。我可以自己研究当前的实现,不需要为此打扰您。但是实现可能会改变,并且还有其他实现。

我之所以问是因为 ThreadLocals:我使用了一个在内部使用 ThreadLocals 的框架。即使是像 company.getName() 这样的简单调用最终也会使用 ThreadLocal。我无法更改该框架的设计方式。至少不是在一段理智的时间内。

该规范在这里似乎令人困惑。包“java.util.stream”的文档指出:

如果行为参数确实有副作用,除非明确说明,否则不能保证这些副作用对其他线程的可见性,也不能保证对同一流管道内“相同”元素的不同操作在同一个线程中执行

...

即使管道被约束以产生与流源的遇到顺序一致的结果(例如, IntStream.range(0,5).parallel().map(x -> x*2).toArray( ) 必须产生 [0, 2, 4, 6, 8]),不保证映射器函数应用于单个元素的顺序,或在哪个线程中为给定元素执行任何行为参数。

我将其解释为:流上的每个操作都可能发生在不同的线程中。但是“forEach”和“forEachOrdered”的文档明确指出:

对于任何给定的元素,可以在库选择的任何时间和任何线程中执行操作。

如果每个流操作都可能发生在未指定的线程中,那么该语句将是多余的。因此是否相反:串行流上的所有操作都保证在当前线程中执行,除了“forEach”和“forEachOrdered”?

我搜索了有关“Java”、“Stream”和“ThreadLocal”组合的权威答案,但一无所获。关闭的事情是Brian Goetz对 Stack Overflow 上一个相关问题的回答,但它是关于顺序,而不是线程,它只是关于“forEach”,而不是其他流方法:Stream.forEach 是否尊重遇到顺序流的顺序?

标签: javajava-8java-streamthread-local

解决方案


我相信您正在寻找的答案没有那么明确,因为它将取决于消费者和/或拆分器及其特征:

在阅读主要引文之前:

https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#stream

默认 Stream stream() 返回以此集合为源的顺序流。当 spliterator() 方法无法返回 IMMUTABLE、CONCURRENT 或后期绑定的拆分器时,应覆盖此方法。(有关详细信息,请参阅 spliterator()。)

https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html#binding

尽管它们在并行算法中有明显的用途,但分离器并不期望是线程安全的。相反,使用拆分器的并行算法的实现应确保拆分器一次仅由一个线程使用。这通常很容易通过串行线程限制来实现,这通常是通过递归分解工作的典型并行算法的自然结果。调用 trySplit() 的线程可以将返回的 Spliterator 移交给另一个线程,该线程又可以遍历或进一步拆分该 Spliterator。如果两个或多个线程在同一个拆分器上同时操作,拆分和遍历的行为是不确定的。如果原始线程将拆分器交给另一个线程进行处理,

Spliterators 和 Consumers 有他们的一组特征,这将定义保证。让我们假设您在一个流中操作。由于拆分器不应该是线程安全的,并且应该将元素处理给可能在其他线程中的其他拆分器,无论是否连续,因此保证为空。但是,如果没有拆分,引号将导致以下情况:在一个拆分器下,操作将保留在同一线程中,任何导致拆分的事件都将导致假设为空,否则为真


推荐阅读