【问题标题】:Understanding snapshots in CopyOnWriteArrayList了解 CopyOnWriteArrayList 中的快照
【发布时间】:2020-08-24 23:21:33
【问题描述】:

我在理解这个概念方面几乎没有问题。请纠正我的错误。

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

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

当迭代已经使用它自己的副本时,有人可以告诉我为什么在修改时需要制作副本。为什么有 2 个应付,一个用于读取,一个用于写入?

我想我说错了,所以请你指出我错过了什么?

【问题讨论】:

    标签: java concurrency snapshot copy-on-write


    【解决方案1】:

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

    迭代器可以使用对数组的引用,因为数组永远不会被修改。 CopyOnWriteArrayList 就像名字所说的那样:当有任何变化时,它会创建一个新的数组副本。如果某个迭代器同时使用旧数组,那不是问题,迭代器将继续使用旧数组,它不会看到对列表所做的任何修改。这使得迭代器“弱一致”。

    【讨论】:

    • 正如您所说:“迭代器将继续使用旧数组,它不会看到对列表所做的任何修改”。现在我的问题.. 如果它没有看到任何修改,为什么必须在副本上进行修改?
    • 啊,这个话题让我很生气:D 当我们刚刚同意迭代器不会看到任何修改时,它会如何被迭代器看到?因为它在生成迭代器时拍摄快照。
    • 迭代器获取的“快照”不是后备数组的副本,它是在创建迭代器时对后备数组的引用。这意味着对数组所做的任何修改都会被迭代器看到。由于我们不希望发生这种情况,这意味着可以对数组进行 no 修改。
    • 谢谢乔尼。还有一件事要问。如果迭代需要 5 秒并且在迭代期间我写了一些东西怎么办。尽管写入是在副本上进行的,但它仍然必须将这些新值设置为原始数组。那么它现在是如何处理的呢?看起来我们的快照毕竟会被改变,即使我们正在修改一个副本。
    • @AnaMaria 为什么你认为“它仍然必须将这些新值设置为原始数组”?这个类的重点是它修改原始数组。
    【解决方案2】:

    CopyOnWriteArrayListjavadoc 表示以下内容:

    “快照”风格的迭代器方法使用对创建迭代器时数组状态的引用。这个数组在迭代器的生命周期内永远不会改变,因此不可能发生干扰,保证迭代器不会抛出ConcurrentModificationException

    你问:

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

    ...

    我想我说错了,所以请你指出我错过了什么?

    javadoc 没有说迭代器复制列表的状态,或制作快照。它表示它使用 reference 到迭代器所在时间点的列表状态。

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

    这显然是你所缺少的。

    提示:1) 始终仔细阅读 javadoc。 2)如果您想确认 javadoc 所说的内容,请阅读源代码。但请记住,源代码可能包含不属于 API 规范的实现细节,并且可能会在不同的 Java 版本之间发生变化。


    跟进问题。

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

    因为如果CopyOnWriteArrayList 没有在每次修改列表时都制作一个新副本,那么现有的迭代器看到修改......定义的方式。

    其他选择是:

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

    请注意,CopyOnWriteArrayList 专为列表阅读最多且很少修改的用例而设计。对于这样的用例,在修改时复制是一个很好的策略。但如果列表需要大量修改,这是一个糟糕的策略……而且CopyOnWriteArrayList 是错误的选择。

    【讨论】:

    • 谢谢,我当然读过文档。问题更像是一种逻辑类型,当我想到它时,我问的问题不大。我会问和 Joni 一样的问题。当迭代器使用快照并且在完成之前看不到任何修改时,为什么需要对副本进行修改?
    • “谢谢,我当然会阅读文档。” - 提示是仔细阅读javadocs :-)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 2019-05-19
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多