Подтвердить что ты не робот

Как 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 является потокобезопасным?

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Сама ссылка на массив помечена как volatile, так что читателям не нужно использовать блокировку для просмотра изменений в ссылочном массиве. (EDIT: Кроме того, volatile гарантирует, что присваивание не будет переупорядочено, что приведет к выполнению задания, когда массив, возможно, находится в несогласованном состоянии.)

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