java - 退出Java并行流工作程序线程后的内存一致性

标签 java multithreading concurrency memory-model java-memory-model

给出以下代码:

final int n = 50;
final int[] addOne = new int[n];
IntStream.range(0, n)
        .parallel()
        .forEach(i -> addOne[i] = i + 1);
// (*) Are the addOne[i] values all visible here?
for (int value : addOne) {
    System.out.println(value);
}

问题:工作线程退出后(即在(*)点),是否可以保证主线程将看到工作线程写入的所有数组内容?

我有兴趣了解Java内存模型对上述问题的看法。这本身与并发问题无关(即Java中的并行流可以按任何顺序处理其元素的事实)。为了抢占一些答复,我知道,如果不使用AtomicReferenceArray<E>这样的内容,就不可能保证两个不同线程之间的内存排序语义能够访问Java中的同一数组元素。出于这个问题的目的,假设并行工作程序将不使用Atomic*类。更重要的是,请注意,因为所有i值都是唯一的,所以没有两个工作线程尝试写入同一数组元素。因此,线程之间的内存排序语义在这里并不重要,仅在并行流结束后,是否由工作线程写入数组元素的任何值对主线程而言始终是可见的。

在初始化主线程中的数组元素与启动并行工作线程之间存在计算上的“障碍”(工作人员最初总是看到初始值为零的元素)。还有一个完成障碍,它等待所有工作人员在流结束时完成操作,然后再将控制权移交给主线程。因此,实际上,当在并行流的末尾施加计算屏障时,是否可以假设总排序还是隐式的“内存刷新屏障”成为的问题。

问另一种方式,主线程是否有可能在点0之后读取某个元素的(*)的默认初始化值?还是CPU缓存层次结构将始终确保主线程将看到工作线程将最新值写入阵列,即使该值尚未从CPU缓存中刷新回RAM呢?

为了这个问题,我假设并行流完成后将控制权返回主线程花费零时间,因此不存在任何竞争条件,不会由于时间而导致将数组值刷新到RAM它需要关闭并行流,或者由于必须关闭并行流而需要进行大量的高速缓存驱逐。

最佳答案

JMM说:

All instance fields, static fields, and array elements are stored in heap memory. In this chapter, we use the term variable to refer to both fields and array elements



这意味着您需要确保在写入和读取数组元素之间存在事前发生的关系。

方法java.util.stream.IntStream#forEach的Javadoc说:

For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization



这意味着您应该在写入和读取数组元素之间强制执行“事前发生”关系,因此不能保证主线程将看到工作线程写入的所有数组内容。

PS:流是一个复杂的框架,实际上,我不确定在您的特定情况下它是否真的不安全,但是契约(Contract)说不能保证您是否访问共享状态(您的数组在调用方线程和工作程序之间共享),而且最好遵循契约(Contract)。

关于java - 退出Java并行流工作程序线程后的内存一致性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60338663/

相关文章:

Java并行查找数组的最小值

ios - 父/子托管对象上下文究竟是如何工作的?

java - Jackrabbit存储库的部署模型

java - 什么时候应该使用各种 DynamoDB API 客户端?

java - 在 Java 中,检查 Selenium WebDriver 是否已退出的最佳方法

c# - 使用线程对 C# 事件中的循环进行计数

c# - 多线程 Web 应用程序

c# - 从另一个线程填充 ListView

java - 如何从 fragment 本身更改 fragment 的 TextView ?

Qt:如何锁定/防止文件在写入时被读取?