CopyOnWriteArrayList 的行为

发布于 2024-10-03 05:51:45 字数 248 浏览 2 评论 0原文

CopyOnWriteArrayList 的 Javadocs 说

ArrayList 的线程安全变体,其中所有可变操作 (添加、设置等)是通过制作新的副本来实现的 底层数组。

我现在很困惑其他线程何时会看到这个新副本中存在的更改?这是否意味着底层数组的副本数量等于集合的突变数量?如果不是这样,这些单独副本的更改何时传输到底层数组以便其他线程可以看到它们?

Javadocs of CopyOnWriteArrayList says

A thread-safe variant of ArrayList in which all mutative operations
(add, set, and so on) are implemented by making a fresh copy of the
underlying array.

I am confused now when will other threads see changes present in this fresh copy? Does this mean there will be number of copies of the underlying array equal to the number of mutations of the collection? If not so when are the changes of these individual copies are transferred to underlying array so that other threads can see them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无所的.畏惧 2024-10-10 05:51:46

这里的想法是,每当您向 CopyOnWriteArrayList 添加或删除时,底层数组基本上都会被复制并进行修改。

这是否意味着会有很多
底层数组的副本相等
到突变的数量
收藏

是的,对于每个更新 ArrayList 的线程,持有旧副本的所有其他线程本质上都会引用不同的数组

这些什么时候改变
单独的副本被转移到
底层数组以便其他线程
能看到他们吗?

您当前正在查看的数组(假设您的迭代器)永远不会改变。当您从数组中读取时,您正在读取它,就像您开始读取时一样。如果另一个线程更改了 CopyOnWriteArrayList,您当前正在观察的数组将不会受到影响。

要获取最新版本,请执行新的读取操作,例如 list.iterator();

也就是说,大量更新此集合会降低性能。如果您尝试对 CopyOnWriteArrayList 进行排序,您将看到列表抛出 UsupportedOperationException(在集合上设置的排序调用 N 次)。仅当您执行 90% 以上的读取时才应使用此读取。

The idea here is that whenever you add or remove to the CopyOnWriteArrayList, the underlying array is basically copied with the modification.

Does this mean there will be number of
copies of the underlying array equal
to the number of mutations of the
collection

Yes, for every thread that updates the ArrayList all other threads holding an older copy will in essence be referencing a different array.

when are the changes of these
individual copies are transferred to
underlying array so that other threads
can see them?

An array you are looking at currently (lets say your iterator) will never change. When you read from an array you are reading it as it was when you started reading. If the CopyOnWriteArrayList changes by another thread, the array you're currently observing will not be effected.

To get the most updated version do a new read like list.iterator();

That being said, updating this collection alot will kill performance. If you tried to sort a CopyOnWriteArrayList you'll see the list throws an UsupportedOperationException (the sort invokes set on the collection N times). You should only use this read when you are doing upwards of 90+% reads.

菊凝晚露 2024-10-10 05:51:46

写时复制数组列表的实现使用底层数组并使用 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 的最新状态。

The implementation for copy on write array list uses the underlying array and accesses it using the setter and getter methods.

/** 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;
}

So for add, set operations it creates copies of the current array (using getArray) and the adds the element and returns back the updated array (using 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();
    }
}

yes the threads that have accessed the arraylist before add or set methods will be having a stale copy of data (which is fine if you can handle some degree of stale data, as CopyOnWriteArrayList is designed with this idea that traversal operations will outnumber adding or update operations) and all the threads that will create an iterator or use get operation will have the latest data of the arrayList.

public E get(int index) {
    return get(getArray(), index);
}

public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

here getarray will give the latest state of the arrayList.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文