gpt4 book ai didi

java - 为什么 CopyOnWriteArrayList 需要写入和读取操作的副本?

转载 作者:行者123 更新时间:2023-12-02 18:49:40 26 4
gpt4 key购买 nike

来自这个article ,它说:

When we areusing any of the modify methods – such as add() or remove() – thewhole content of the CopyOnWriteArrayList is copied into the newinternal copy.

Due to this simple fact, we can iterate over the list in a safe way,even when concurrent modification is happening.

When we're calling the iterator() method on the CopyOnWriteArrayList,we get back an Iterator backed up by the immutable snapshot of thecontent of the CopyOnWriteArrayList.

Its content is an exact copy of data that is inside an ArrayList fromthe time when the Iterator was created. Even if in the meantime someother thread adds or removes an element from the list, thatmodification is making a fresh copy of the data that will be used inany further data lookup from that list.

接下来要问自己的一个简单问题是为什么两者都是?基本上,根据我的理解,写入操作是在新副本上进行的,而读取操作是在集合的克隆上完成的。

例如,如果写入是在新副本上完成的,这意味着我可以迭代“原始”集合 - 这意味着它不会受到影响。那么为什么要增加在另一个副本(快照)中存储元素的开销呢?或者相反,如果我将元素存储在副本(快照)中,当我实际上是在克隆而不是“原始”集合(意味着快照永远不会改变)上迭代时,为什么需要在副本上进行写入?

我希望这个问题是合法的,因为我确实检查了互联网上所有可能的来源,但没有一篇文章能帮助我消除这种困惑。我在这里缺少什么?

最佳答案

当您调用 iterator 时,

CopyOnWriteArrayList 不会创建数组的副本,因为 docs说:

The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created.

注意“引用”这个词。

这句话的措辞相当糟糕:

Its content is an exact copy of data that is inside an ArrayList from the time when the Iterator was created.

这并不意味着在您调用 iterator()创建了数组的副本。应该说:

Its content is the same as the data that is inside an ArrayList from the time when the Iterator was created.

该段比较重要的一点是:

Even if in the meantime some other thread adds or removes an element from the list, that modification is making a fresh copy of the data that will be used in any further data lookup from that list.

这意味着如果您创建一个迭代器,然后继续以某种方式改变列表,迭代器将看不到这些更改。为什么?因为突变是通过创建一个具有突变的新数组来完成的,但是迭代器正在遍历没有突变的旧数组。这就是为什么我们说迭代器拍摄“快照”。

这里有一些来自 OpenJDK 的代码来说明。

iterator() ,它只是创建了一个带有 getArray()COWIterator ,它通过返回 volatile array 字段来获取快照:

final Object[] getArray() {
return array;
}

...

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

和增变器方法,例如add ,设置数组字段:

final void setArray(Object[] a) {
array = a;
}

...

public boolean add(E e) {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}

我删除了(解锁)锁定代码,以便更容易看到发生了什么。

关于java - 为什么 CopyOnWriteArrayList 需要写入和读取操作的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66953297/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com