【发布时间】:2020-06-05 21:13:09
【问题描述】:
给定以下代码:
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 的竞争条件取决于关闭并行流所需的时间,或者由于关闭并行流所必须进行的缓存驱逐量。
【问题讨论】:
-
@vicpermir 不是真的,这似乎无关。
-
顺便说一句,如果
N不是常数,应该写成小写... -
由于遭遇订单不在其中发挥作用,问题归结为
forEach调用中给出的消费者是否已针对@的所有元素执行987654332@。我确定是这样(尽管我不会为此寻找权威答案)。 -
我无法将其作为重复项关闭,但here you go
标签: java multithreading concurrency memory-model java-memory-model