CopyOnWriteArrayList的Javadocs说
:ArrayList的一个线程安全变体,在该变体中,所有的可变操作(添加、设置等)都是通过创建基础数组的新副本来实现的。
我现在很困惑,其他线程什么时候会看到这个新副本中出现的变化?这是否意味着基础数组的拷贝数将等于集合的突变数?如果不是,何时将这些单独副本的更改转移到基础数组,以便其他线程能够看到它们?
发布于 2010-11-19 17:54:26
这里的想法是,每当您向CopyOnWriteArrayList添加或删除时,基础数组基本上都是通过修改复制的。
是否意味着基础数组的拷贝数等于集合的突变数。
是的,对于每个更新ArrayList的线程,所有其他包含旧副本的线程本质上都将引用不同的数组。
何时将这些单独副本的更改传输到基础数组,以便其他线程能够看到它们?
当前正在查看的数组(假设您的迭代器)永远不会改变。当您从数组读取时,您正在读取它,就像开始读取时一样。如果CopyOnWriteArrayList由另一个线程更改,则不会影响当前正在观察的数组。
要获得最新版本,请执行类似于list.iterator();的新读取
尽管如此,更新这个集合会破坏性能。如果您尝试对一个CopyOnWriteArrayList排序,您将看到列表抛出一个UsupportedOperationException (排序调用集合上的设置N次)。只有在向上读取90+%时,才应该使用此读取。
发布于 2018-07-13 08:40:51
写数组列表的复制实现使用基础数组,并使用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;
}因此,对于add,设置操作,它创建当前数组的副本(使用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();
}
}是的,在添加或设置方法之前访问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的最新状态。
https://stackoverflow.com/questions/4227827
复制相似问题