确保流的工作线程完成后的内存一致性



如何确保在工作线程完成处理以下代码中的流后,即在第(*)点,strValues数组的内容是一致的,即(*)的主线程在任何strValues数组条目中都没有看到过时的null值?

int N = 50;
String[] strValues = new String[N];
IntStream.range(0, N)
.parallel()
.forEach(i -> strValues[i] = Integer.toString(i));
// (*) Is strValues consistent here?
for (String strValue : strValues) {
System.out.println(strValue);
}

(当然,我可以使用.map(...)而不是.forEach(...)返回Integer.toString(i),但这不是我在这里要说明的内容,出于效率原因或因为您必须让工作线程设置或返回许多不同的值,它并不总是一个可用的选项。

在实践中,我从未见过当从一个线程设置数组条目然后从另一个线程读取数组条目时nullstrValues数组条目的等效物。但是,我知道Java的内存模型不能保证这一点,因此存在AtomicInteger等。

但是在Java中没有AtomicArray等价物。您能做的最好的事情是使用空AtomicReference对象初始化AtomicReference[]数组,并从工作线程设置它们。但这在 CPU 资源和内存上效率非常低。

也没有办法可靠地刷新CPU的所有缓存,无论是从Java还是可能根本没有。

我知道存在栅栏以防止内存操作重新排序,但我认为它们不会改变内存刷新语义。还是内存操作重新排序是导致 Java 内存模型需要Atomic*类的唯一问题?

我的主要问题:可以做些什么来从一个线程(或一组工作线程(设置数组中的值,然后尝试从另一个线程读取它们,而不会遇到过时的值?我特别想知道当工作线程到达流的末尾时是否设置了适当的内存围栏或类似设置。(我不明白如何自动完成此操作,所以我猜它不会发生。

但是

在Java中没有AtomicArray等价物。

是的,有:AtomicReferenceArray<E>.

int N = 50;
AtomicReferenceArray<String> strValues = new AtomicReferenceArray<>(N);
IntStream.range(0, N)
.parallel()
.forEach(i -> strValues.set(i, Integer.toString(i)));
for (int i = 0; i < strValues.length(); i++) {
System.out.println(strValues.get(i));
}

这个答案是从Java易失数组派生出来的吗?

最新更新