2010-06-01 3 views
48

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

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

ответ

61

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

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

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

+0

Спасибо. Я пропустил тот факт, что массив «volatile». – Fixpoint

+15

Важной деталью является то, что volatile применяется только к самой ссылке массива, а не к содержимому массива. Однако, поскольку все изменения в массиве производятся ** до того, как ** его ссылка опубликована, летучие гарантии распространяются на содержимое массива. – assylias

16

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

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

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

+1

Это не соответствует 100%. Атомность установки ссылки недостаточна для обеспечения согласованности, и правила модели Java-памяти решают эту проблему. Неверный порядок записи и переупорядочение инструкций может произойти, а затем поток может получить ссылку, указывающую на непоследовательный объект. Это также происходит с шаблоном двойной проверки-блокировки (см. Http://www.ibm.com/developerworks/java/library/j-dcl.html) –

+3

Это не то же самое. чтение/запись в изменчивое состояние - это то, что JMM означает «синхронизированное действие» и определяет барьер на том, что можно изменить. См. Http://java.sun.com/docs/books/jls/third_edition/html/memory.html – mdma

+1

@Eyal Schneider: Добро пожаловать в 2004 году (см. Http://www.ibm.com/developerworks/library/j- jtp03304 /). Прочтите раздел «Новые гарантии для летучих» –

 Смежные вопросы

  • Нет связанных вопросов^_^