CopyOnWriteArrayList
的Javadocs说
ArrayList的线程安全变体,其中包含所有可变操作 (添加,设置等)是通过制作新的副本来实现的 底层数组。
我很困惑,其他线程何时会看到此新副本中存在的更改?这是否意味着底层数组的副本数量等于集合的突变数量?如果不是这样的话,何时将这些单个副本的更改传输到基础数组,以便其他线程可以看到它们?
答案 0 :(得分:17)
这里的想法是,无论何时添加或删除CopyOnWriteArrayList
,底层数组基本上都会随修改一起复制。
这是否意味着会有多少 底层数组的副本相等 到了突变的数量 集合
是的,对于更新ArrayList
的每个线程,持有旧版本的所有其他线程本质上都会引用不同的数组。
这些变化何时发生 个人副本转移到 底层数组使其他线程 可以看到他们吗?
您当前正在查看的数组(假设您的迭代器)将永远不会更改。从数组中读取时,您正在阅读它,就像您开始阅读时一样。如果CopyOnWriteArrayList
由另一个线程更改,则您当前正在观察的阵列将不受影响。
要获得最新版本,请执行list.iterator();
话虽如此,更新此系列将会扼杀性能。如果您尝试对CopyOnWriteArrayList
进行排序,则会看到列表抛出UsupportedOperationException
(对集合调用的排序调用次数为N次)。只有在读取90%以上的读数时才应该使用此读数。
答案 1 :(得分:0)
在写数组列表上复制的实现使用底层数组,并使用setter和getter方法对其进行访问。
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
因此,对于添加,设置操作,它会创建当前数组的副本(使用getArray),并添加元素并返回更新后的数组(使用setArray)。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
是,在add或set方法之前访问过arraylist的线程将具有陈旧的数据副本(如果可以处理一定程度的陈旧数据,这很好,因为CopyOnWriteArrayList的设计思路是遍历操作将胜过添加或更新操作),所有将创建迭代器或使用get操作的线程将具有arrayList的最新数据。
public E get(int index) {
return get(getArray(), index);
}
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
在这里,getarray将给出arrayList的最新状态。