Как CopyOnWriteArrayList может быть ориентирован на многопотоковое исполнение?

Я смотрел в исходный код OpenJDK CopyOnWriteArrayList и кажется, что все операции записи защищены той же блокировкой, и операции чтения не защищены вообще. Как я понимаю под JMM, все доступы к переменной (и чтение и запись) должны быть защищены блокировкой, или эффекты переупорядочения могут произойти.

Например, set(int, E) метод содержит эти строки (под блокировкой):

/* 1 */ int len = elements.length;
/* 2 */ Object[] newElements = Arrays.copyOf(elements, len);
/* 3 */ newElements[index] = element;
/* 4 */ setArray(newElements);

get(int) метод, с другой стороны, только делает return get(getArray(), index);.

В моем понимании JMM это означает это get может наблюдать массив в непоследовательном состоянии, если операторы 1-4 переупорядочиваются как 1-2 (новых)-4-2 (copyOf)-3.

Сделайте я понимаю JMM неправильно или есть ли любые другие объяснения на почему CopyOnWriteArrayList ориентировано на многопотоковое исполнение?

56
задан Basil Bourque 14 August 2014 в 22:27
поделиться

2 ответа

Если вы посмотрите на ссылку на базовый массив, вы увидите, что он помечен как volatile . Когда происходит операция записи (например, в приведенном выше фрагменте), эта энергозависимая ссылка обновляется только в последнем операторе через setArray . До этого момента любые операции чтения будут возвращать элементы из старой копии массива.

Важным моментом является то, что обновление массива является атомарной операцией , и поэтому при чтении всегда будет отображаться массив в согласованном состоянии.

Преимущество снятия блокировки только для операций записи заключается в улучшенной пропускной способности для операций чтения: это связано с тем, что операции записи для CopyOnWriteArrayList потенциально могут быть очень медленными, поскольку они включают копирование всего списка.

70
ответ дан 26 November 2019 в 17:25
поделиться

Получение ссылки на массив - это атомарная операция. Таким образом, читатели увидят либо старый массив, либо новый массив - в любом случае состояние будет согласованным. ( set (int, E) вычисляет новое содержимое массива перед установкой ссылки, поэтому массив согласован при присвоении.)

Сама ссылка на массив помечена как volatile , так что читателям не нужно использовать блокировку для просмотра изменений в указанном массиве. (РЕДАКТИРОВАТЬ: Кроме того, volatile гарантирует, что назначение не будет переупорядочено, что приведет к выполнению назначения, когда массив, возможно, находится в несогласованном состоянии.)

Блокировка записи требуется для предотвращение одновременной модификации, которая может привести к потере несогласованных данных или изменений в массиве.

19
ответ дан 26 November 2019 в 17:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: