了解CopyOnWriteArrayList

问题描述

在理解这个概念方面我没有什么问题。请纠正我错了的地方。

基本上,迭代创建原始数组的快照(副本),因此修改集合的线程不会影响我们的迭代,因为迭代使用副本。所以这里没有ConcurrentException,很好。

但是我还读到,任何修改都是通过制作原始集合的副本并使用该副本进行更改来完成的。然后将其设置为原始地址。

有人可以告诉我为什么修改时,迭代已经使用自己的副本时需要制作副本。为什么要有2种应对措施,一种用于阅读,一种用于书写?

我认为我说的话不正确,所以请您指出我想念的是什么?

解决方法

CopyOnWriteArrayList创建迭代器时,“快照”是对其当前数组的引用,而不是该数组的副本。

迭代器可以使用对数组的引用,因为从不修改数组。 CopyOnWriteArrayList的名称说明了这一点:更改任何内容后,它将为该数组创建一个新副本。如果某个迭代器同时使用旧数组,这不是问题,则迭代器将继续使用旧数组,并且不会看到对该列表所做的任何修改。这使迭代器“弱一致”。

,

CopyOnWriteArrayList的{​​{3}}说:

“快照”样式的迭代器方法在创建迭代器时使用对数组状态的引用。该数组在迭代器的生命周期内永不更改,因此不会发生干扰,并且保证迭代器不会抛出ConcurrentModificationException

您问:

基本上,迭代创建原始数组的快照(副本),因此修改集合的线程不会影响我们的迭代,因为迭代使用副本。所以这里没有ConcurrentException,很好。

...

我认为我说的话不正确,所以请您指出我想念的是什么?

javadoc不会说迭代器会复制列表状态,也不会做出快照。它说,它在迭代器的时间点使用引用到列表的状态。

当列表发生突变时,就会发生状态复制,如javadoc中其他地方所述。

这显然是您所缺少的。

提示:1)务必仔细阅读Javadoc。 2)如果要确认javadoc所说的内容,请阅读源代码。但是请记住,源代码可能包含API规范中未包含的实现细节,并且可能在不同的Java版本之间进行更改。


后续问题。

当迭代器使用快照并且在完成之前看不到任何修改时,为什么需要在副本上进行修改?

因为如果CopyOnWriteArrayList 没有每次修改列表时都进行新拷贝,则现有迭代器看到修改...不明确的方式。

其他替代方法是:

  1. 在迭代列表时,请使用锁来阻止任何修改。这将是并发瓶颈。
  2. 每次启动迭代时创建快照。这也将是并发瓶颈,因为您需要在创建快照时阻止修改。

请注意,CopyOnWriteArrayList是为清单最易读取且很少修改的用例设计的。对于此类用例,修改后复制是一个不错的策略。但是,如果需要对列表进行大量修改,则这是一个错误的策略……而CopyOnWriteArrayList是错误的选择。