Учитывая следующий код:
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
уникальны. Поэтому семантика упорядочения памяти между потоками здесь не важна, а важна только то, будет ли какое-либо значение, записанное в элемент массива рабочим потоком, всегда быть видимым для основного потока после завершения параллельного потока.
Существует вычислительный «барьер» между инициализацией элементов массива в основном потоке и запуском параллельных рабочих потоков (рабочие всегда сначала будут видеть элементы с нулевым значением инициализатора). И есть барьер завершения, который ожидает завершения всех рабочих процессов в конце потока, прежде чем передать управление обратно основному потоку. Таким образом, на самом деле вопрос сводится к можно ли предполагать полное упорядочение или неявный «барьер сброса памяти», когда вычислительный барьер накладывается в конце параллельного потока.
На другой вопрос: есть ли вообще шанс, что основной поток может прочитать значение инициализации по умолчанию 0
для некоторого элемента после точки (*)
? Или иерархия кеш-памяти ЦП всегда будет гарантировать, что основной поток увидит самое последнее значение, записанное в массив рабочим потоком, даже если это значение еще не было сброшено из кеш-памяти ЦП обратно в ОЗУ?
Для целей этого вопроса я предполагаю, что для возврата управления в основной поток после завершения параллельного потока требуется нулевое время, поэтому нет состояния гонки, которое могло бы привести к тому, что значения массива будут сброшены в ОЗУ из-за времени это требуется для выключения параллельного потока, или из-за количества вытеснения кеша, которое должно произойти, чтобы выключить параллельный поток.
N
не является константой, его нужно писать в нижнем регистре ... - person dan1st   schedule 23.02.2020forEach
, выполнен для всех элементовStream
. Я уверен, что это так (хотя я не буду искать на это авторитетного ответа). - person daniu   schedule 24.02.2020