来自 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)已被修改,而不是在迭代过程开始时创建的数组。
那么,作者的意思是什么 -
返回元素与迭代器时的元素完全相同 创建,无论后续修改如何。
答案 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]
现在您创建了一个COWIterator
,snapshot
引用了相同的数组:
元素 - &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已经引用了较旧的快照数组,因此它甚至不知道正在更新的数组。
希望解释。