为什么 CopyOnWriteArrayList 需要对写入和读取操作进行复制?

如何解决为什么 CopyOnWriteArrayList 需要对写入和读取操作进行复制?

来自这个 article,它说:

当我们 使用任何修改方法——例如 add() 或 remove()—— copyOnWriteArrayList 的全部内容被复制到新的 内部副本。

由于这个简单的事实,我们可以安全地遍历列表, 即使发生并发修改

当我们在 copyOnWriteArrayList 上调用 iterator() 方法时, 我们得到一个由不可变快照备份的迭代器 copyOnWriteArrayList 的内容

它的内容是 ArrayList 中数据的精确副本,来自 迭代器的创建时间。即使在此期间有些 其他线程从列表中添加删除一个元素,即 修改正在制作将用于的数据的新副本 从该列表中进一步查找数据。

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

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

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

解决方法

CopyOnWriteArrayList 在您调用 iterator 时不会创建数组的副本,正如 docs 所说:

“快照”样式的迭代器方法使用对创建迭代器时数组状态的引用

注意“参考”这个词。

这句话的措辞相当糟糕:

它的内容是从创建迭代器时起在 ArrayList 中的数据的精确副本。

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

它的内容从创建迭代器时起在ArrayList中的数据相同。

该段更重要的一点是:

即使在此期间某个其他线程从列表中添加或删除了一个元素,该修改也会制作数据的新副本,该副本将用于从该列表中进行任何进一步的数据查找。

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

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

iterator()中,它简单地创建了一个带有getArray()COWIterator,它通过返回易失性array字段来获取快照:

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

...

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

和 mutator 方法,例如 add,设置 array 字段:

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

我删除了(解锁)锁定代码,以便于查看发生了什么。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?