2016-10-04 4 views
0

Абстрактных от JCIPитераторов для коллекций копирования при записи

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

Итераторов, возвращаемых коллекциями копирования при записи не бросают ConcurrentModificationException и возвращают элементы точно так, как они были на момент создания итератора, независимо от последующих модификаций

Глядя на исходный код на #CopyOnWriteArrayList.iterator()

956   public Iterator<E> iterator() { 
957    return new COWIterator<E>(getArray(), 0); // call to private constructor 
958   } 

993   private final Object[] snapshot; 

994 
995   private int cursor; 
996 
997   private COWIterator(Object[] elements, int initialCursor) { 
998    cursor = initialCursor; 
999    snapshot = elements; 
1000  } 

Вы видите, что snapshot указывает на массив, возвращаемый getArray(), и поскольку возвращаемая ссылка массива нестабильна, любое изменение ссылочной переменной гарантированно будет отражено. (Создание ссылки на массив неустойчивой не делает на каждое месте индекса летучих элементов)

, где изменение в массиве выполняются внутренней сторонка

386  public E More ...set(int index, E element) { 
387   final ReentrantLock lock = this.lock; 
388   lock.lock(); 
389   try { 
390    Object[] elements = getArray(); 
391    E oldValue = get(elements, index); 
392 
393    if (oldValue != element) { 
394     int len = elements.length; 
395     Object[] newElements = Arrays.copyOf(elements, len); 
396     newElements[index] = element; 
397     setArray(newElements); 
398    } else { 
399     // Not quite a no-op; ensures volatile write semantics 
400     setArray(elements); 
401    } 
402    return oldValue; 
403   } finally { 
404    lock.unlock(); 
405   } 
406  } 

setArray(newElements); т.е., как показано в способе.

где GetArray()

92  final Object[] More ...getArray() { 
93   return array; 
94  } 

& setArray (...)

final void More ...setArray(Object[] a) { 
100   array = a; 
101  } 

являются операции на этом неустойчивом массиве

private volatile transient Object[] array; 

Clearl y, итератор возвращает массив, который (if) был изменен, а не тот, который был создан в начале процесса итерации.

Итак, что же автор щенного

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

+0

Я посмотрел код, который вы разместили здесь, и ясно, итератор возвращает массив, который никогда не был изменен. –

+0

@MarkoTopolnik: Но в то же время другие потоки могут явно модифицировать его через 'set (int index, E element) .' –

+0

*« Очевидно, что итератор возвращает массив »* Ehh, nooo. «Ясно», итератор * использует * массив, полученный во время создания итератора *. Любая последующая модификация списка создаст * новый * массив, поэтому итератор не увидит эти изменения. – Andreas

ответ

4

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

Это не имеет никакого отношения к volatile семантика.Ссылка на массив создается при вызове конструктора COWIterator (при создании итератора). Создание новой копии массива и присвоение его ссылки на поле array будет содержать snapshot итератора, указывающего на ту же старую ссылку.

+0

Вот что я написал - моментальный снимок указывает на массив, возвращаемый getArray() –

+0

@ShirgillFarhanAnsari Да, но 'setArray' изменяет ссылку на совершенно другой массив. Вы должны прочитать [this] (http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value). – manouti

+1

@ShirgillFarhanAnsari Точно! Вызывается массив * в момент * 'getArray()'. Последующие вызовы 'getArray()' могут возвращать другой массив, но 'getArray()' никогда не вызывается снова 'Iterator' * во время * итерации. – Andreas

1

Допустим, вы создании экземпляра CopyOnWriteArrayList, теперь elements ссылается массив:

элементы -> [0, 1, 2]

Теперь вы создаете COWIterator, snapshot ссылается тот же массив:

элементы -> [0, 1, 2] < - снимок

Теперь вы добавляете новый элемент в CopyOnWriteArrayList, нового массив создан и ссылка обновляются для нового массива:

снимки -> [0, 1, 2]

элементов -> [0, 1, 3]

Так, snapshot все еще указывает на начальный массив!

+0

Это была проблема языкового барьера для меня. Я полностью неверно истолковал это заявление. –

0

Код, который вы вставили здесь, содержит эти строки (не скопированные несколько строк между ними): Object [] elements = getArray(); Объект [] newElements = Arrays.copyOf (элементы, len); newElements [index] = element;

Строка 1 относится к существующему массиву, который поддерживает эту коллекцию. Если вы попытаетесь получить итератор до обновления коллекции, то это массив (именуемый моментальным снимком), который получает итератор.

Линия 2 представляет собой новый массив создается путем копирования (отсюда и КПС), а затем новый элемент добавляется к нему, как показан в строке 3.

Этого newElements массив затем присваиваются летучей ссылка на массив. Итератор уже ссылается на старый массив снимков, поэтому он даже не знает о обновляемом массиве.

Надеюсь, что объясняет.

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