Учитывая следующий код:
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 могут обрабатывать свои элементы в любом порядке). Чтобы исключить некоторые ответы, я знаю, что невозможно гарантировать семантику упорядочения памяти между двумя разными потоками с доступом к одному и тому же элементу массива в Java без использования чего-то вроде AtomicReferenceArray<E>
. Для целей этого вопроса предположим, что Atomic*
классы не будут использоваться параллельными работниками. Что еще более важно, обратите внимание, что никакие два рабочих потока никогда не пытаются писать в один и тот же элемент массива, поскольку все значения i
являются уникальными. Поэтому семантика упорядочения памяти между потоками здесь не важна, только будет ли любое значение, записанное в элемент массива рабочим потоком, всегда видимым для основного потока после завершения параллельного потока.
Существует вычислительный «барьер» между инициализацией элементов массива в главном потоке и запуском параллельных рабочих потоков (рабочие всегда первоначально увидят элементы с нулевым значением инициализатора). И существует барьер завершения, который ожидает завершения всех рабочих в конце потока, прежде чем передать управление обратно в основной поток. Таким образом, на самом деле вопрос сводится к , можно ли предположить полное упорядочение или неявный «барьер памяти» sh, когда на конец параллельного потока накладывается вычислительный барьер .
Спрашивается Иначе, есть ли вероятность, что основной поток может прочитать значение инициализации по умолчанию 0
для некоторого элемента после точки (*)
? Или иерархия кэша ЦП всегда гарантирует, что основной поток увидит самое последнее значение, записанное в массив рабочим потоком, даже если это значение еще не было выгружено из кэша ЦП обратно в ОЗУ?
Я предполагаю, что для целей этого вопроса требуется нулевое время для возврата управления основному потоку после завершения параллельного потока, поэтому не существует условия гонки, которое приводит к сбросу значений массива в ОЗУ из-за время, необходимое для отключения параллельного потока, или из-за объема вытеснения из кэша, необходимого для отключения параллельного потока.