Как CopyOnWriteArrayList может быть поточно-ориентированным? - PullRequest
54 голосов
/ 01 июня 2010

Я посмотрел на Исходный код 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 является поточно-ориентированным?

Ответы [ 2 ]

68 голосов
/ 01 июня 2010

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

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

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

18 голосов
/ 01 июня 2010

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

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

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

...