用于写时复制集合的迭代器

时间:2016-10-04 08:49:53

标签: java

来自 JCIP

的摘要
  

写时复制集合的迭代器保留对该引用的引用   支持在迭代开始时当前的数组,从那以后   这永远不会改变,他们只需要简单地同步   确保数组内容的可见性。

     

copy-on-write集合返回的迭代器不会抛出   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()返回的数组,并且由于返回的数组引用是volatile,因此保证反映引用变量的任何更改。 (使数组引用volatile不会使每个索引位置的元素变得不稳定

数组的更改在里面完成 -

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;

显然,迭代器返回的数组(if)已被修改,而不是在迭代过程开始时创建的数组。

那么,作者的意思是什么 -

  

返回元素与迭代器时的元素完全相同   创建,无论后续修改如何。

3 个答案:

答案 0 :(得分:4)

  

由于返回的数组是volatile,因此可以保证反映参考变量的任何更改。

这与COWIterator语义无关。调用array构造函数时(创建迭代器时)将创建对数组的引用。创建数组的新副本并将其引用分配给snapshot字段将使迭代器的public abstract class SomeObject { public abstract boolean isNil(); } public class NullObject extends SomeObject { @Override public boolean isNil() { return true; } } public class RealObject extends SomeObject { @Override public boolean isNil() { return false; } } 指向相同的旧引用。

答案 1 :(得分:1)

我们假设您实现CopyOnWriteArrayList,现在elements正在引用数组:

元素 - &gt; [0,1,2]

现在您创建了一个COWIteratorsnapshot引用了相同的数组:

元素 - &gt; [0,1,2]&lt; - snapshot

现在您向CopyOnWriteArrayList添加了一个新元素,创建了一个 new 数组,并将引用更新为新数组:

快照 - &gt; [0,1,2]

元素 - &gt; [0,1,3]

所以,snapshot仍然指向初始数组!

答案 2 :(得分:0)

您在此处粘贴的代码包含以下行(不会在其间复制几行):     Object [] elements = getArray();     Object [] newElements = Arrays.copyOf(elements,len);     newElements [index] = element;

第1行指的是支持此集合的现有数组。如果在更新集合之前尝试获取迭代器,则这是迭代器获取的数组(称为快照)。

第2行是通过复制(因此是COW)创建的新数组,然后添加新项目,如第3行所示。

然后将此newElements数组分配给易失性数组引用。 Iterator已经引用了较旧的快照数组,因此它甚至不知道正在更新的数组。

希望解释。