ConcurrentHashMap的弱一致迭代器

时间:2016-03-18 08:18:24

标签: java concurrency concurrenthashmap

Java Concurrency in Practice提到:

  

ConcurrentHashMap返回的迭代器非常一致   比失败快。弱一致的迭代器可以容忍   并发修改,遍历元素时存在的元素   构造迭代器,并且可能(但不保证)反映迭代器构造后对集合的修改。

  1. 如何使迭代器弱一致或故障安全有助于并发环境,因为ConcurrentHashMap的状态仍将被修改。唯一的问题是它不会抛出ConcurrentModificationException
  2. 为什么创建故障安全迭代器时集合返回的失败快速迭代器有利于并发。

3 个答案:

答案 0 :(得分:3)

TL; DR:因为锁定。

如果你想要一致的迭代器,那么你必须将所有修改锁定到Map - 这在并发环境中是一个巨大的惩罚。

如果这是您想要的,您当然可以手动执行此操作,但迭代Map不是它的目的,因此默认行为允许在迭代时进行并发写入。

相同的参数不适用于普通集合,它们仅由(允许)由单个线程访问。对ArrayList的迭代预期是一致的,因此失败的快速迭代器可以实现一致性。

答案 1 :(得分:3)

在您的特定情况下的正确性

请记住,快速失败迭代器会迭代原始集合。

相比之下失败安全(a.k.a 弱一致)迭代器会迭代原始集合的副本。因此,对原始集合的任何更改都不会引起注意,并且它保证了ConcurrentModificationException s的缺失。


回答你的问题:

  1. 使用失败安全迭代器有助于实现并发性,因为您不必阻止整个集合上的读取线程。在阅读发生时,可以在下面修改集合。缺点是读取线程将集合的状态视为在创建迭代器时拍摄的快照。
  2. 如果上述限制对您的特定用例不利(您的读者应始终看到相同的集合状态),则必须使用 Fail Fast 迭代器并保持对集合的并发访问权限控制得更紧。
  3. 正如您所看到的,在用例的正确性和速度之间进行权衡。

    的ConcurrentHashMap

    ConcurrentHashMap CHM )利用多种技巧来提高访问的并发性。

    • 首先 CHM 实际上是多个地图的分组;每个MapEntry都存储在多个中的一个中,每个都是一个可以同时读取的哈希表(read方法不会阻止)。
    • 的数量是3参数构造函数中的最后一个参数,它被称为concurrencyLevel(默认 16 )。段数决定了整个数据中并发写入器的数量。通过附加的内部散列算法确保了段之间条目的相等扩展。
    • 每个HashMapEntry s值为volatile,从而确保竞争修改和后续读取的细粒度一致性;每次阅读都反映了最近完成的更新
    • 迭代器和枚举失败安全 - 反映自迭代器/枚举创建以来某个时刻的状态;这允许以降低一致性为代价同时进行读取和修改。

答案 2 :(得分:1)

首先,并发集合的迭代器不是fail-safe,因为它们没有故障模式,它们可以通过某种紧急程序以某种方式处理。他们根本不会失败。

非并发集合的迭代器是快速失败的,因为性能原因它们的设计方式不允许修改它们迭代的集合的内部结构。例如。一个hashmap的迭代器不知道如何在调整hashmap大小后重新调整后继续迭代。

这意味着它们不会因为其他线程访问它们而失败,如果当前线程执行的修改使迭代器的假设无效,它们也会失败。

这些集合不是忽略那些麻烦的修改并返回不可预测和损坏的结果,而是尝试跟踪修改并在迭代期间抛出异常以通知程序员出错。这称为fail-fast

那些快速失败的机制也不是线程安全的。这意味着如果非当前线程没有发生非法修改,而是从不同的线程发生,则不能保证不再检测到它们。在这种情况下,它只能被认为是尽力而为的故障检测机制。

另一方面,并​​发集合的设计必须能够同时处理多个写入和读取,并且底层结构会不断变化。

因此,迭代器不能总是假设在迭代期间永远不会修改底层结构。

相反,它们旨在提供较弱的保证,例如迭代过时的数据,或者也可能显示迭代器创建后发生的一些但不是所有更新。这也意味着他们可能会在单个线程内的迭代过程中修改过时的数据,这对于程序员来说可能有点违反直觉,因为人们通常希望在单个线程中立即看到修改。

示例:

HashMap:尽力而为的失败快速迭代器。

  • 迭代器支持删除
  • 来自同一线程的
  • 结构修改,例如迭代期间clear() Map:保证在下一个迭代器步骤中抛出ConcurrentModificationException
  • 迭代期间来自不同线程的
  • 结构修改:迭代器通常会抛出异常,但也可能导致不一致,不可预测的行为

CopyOnWriteArrayList:快照迭代器

  • 迭代器不支持删除
  • 迭代器显示创建时冻结的项目的视图
  • 集合可以在迭代期间由任何线程修改,包括当前线程,而不会导致异常,但它对迭代器访问的项目没有影响
  • clear()列表不会停止迭代
  • iterator永远不会抛出CME

ConcurrentSkipListMap:弱一致的迭代器

  • 迭代器支持删除,但可能会导致令人惊讶的行为,因为它完全基于Map键,而不是当前值
  • 迭代器可能会看到自创建以来发生的更新,但无法保证。这意味着,例如clear()地图可能会或可能不会停止迭代,删除条目可能会或可能不会阻止它们在剩余的迭代期间显示
  • iterator永远不会抛出CME