Java Concurrency in Practice提到:
ConcurrentHashMap
返回的迭代器非常一致 比失败快。弱一致的迭代器可以容忍 并发修改,遍历元素时存在的元素 构造迭代器,并且可能(但不保证)反映迭代器构造后对集合的修改。
ConcurrentHashMap
的状态仍将被修改。唯一的问题是它不会抛出ConcurrentModificationException
。答案 0 :(得分:3)
如果你想要一致的迭代器,那么你必须将所有修改锁定到Map
- 这在并发环境中是一个巨大的惩罚。
如果这是您想要的,您当然可以手动执行此操作,但迭代Map
不是它的目的,因此默认行为允许在迭代时进行并发写入。
相同的参数不适用于普通集合,它们仅由(允许)由单个线程访问。对ArrayList
的迭代预期是一致的,因此失败的快速迭代器可以实现一致性。
答案 1 :(得分:3)
请记住,快速失败迭代器会迭代原始集合。
相比之下失败安全(a.k.a 弱一致)迭代器会迭代原始集合的副本。因此,对原始集合的任何更改都不会引起注意,并且它保证了ConcurrentModificationException
s的缺失。
回答你的问题:
正如您所看到的,在用例的正确性和速度之间进行权衡。
ConcurrentHashMap
( CHM )利用多种技巧来提高访问的并发性。
MapEntry
都存储在多个段中的一个中,每个都是一个可以同时读取的哈希表(read
方法不会阻止)。concurrencyLevel
(默认 16 )。段数决定了整个数据中并发写入器的数量。通过附加的内部散列算法确保了段之间条目的相等扩展。HashMapEntry
s值为volatile
,从而确保竞争修改和后续读取的细粒度一致性;每次阅读都反映了最近完成的更新答案 2 :(得分:1)
首先,并发集合的迭代器不是fail-safe,因为它们没有故障模式,它们可以通过某种紧急程序以某种方式处理。他们根本不会失败。
非并发集合的迭代器是快速失败的,因为性能原因它们的设计方式不允许修改它们迭代的集合的内部结构。例如。一个hashmap的迭代器不知道如何在调整hashmap大小后重新调整后继续迭代。
这意味着它们不会因为其他线程访问它们而失败,如果当前线程执行的修改使迭代器的假设无效,它们也会失败。
这些集合不是忽略那些麻烦的修改并返回不可预测和损坏的结果,而是尝试跟踪修改并在迭代期间抛出异常以通知程序员出错。这称为fail-fast。
那些快速失败的机制也不是线程安全的。这意味着如果非当前线程没有发生非法修改,而是从不同的线程发生,则不能保证不再检测到它们。在这种情况下,它只能被认为是尽力而为的故障检测机制。
另一方面,并发集合的设计必须能够同时处理多个写入和读取,并且底层结构会不断变化。
因此,迭代器不能总是假设在迭代期间永远不会修改底层结构。
相反,它们旨在提供较弱的保证,例如迭代过时的数据,或者也可能显示迭代器创建后发生的一些但不是所有更新。这也意味着他们可能会在单个线程内的迭代过程中修改过时的数据,这对于程序员来说可能有点违反直觉,因为人们通常希望在单个线程中立即看到修改。
示例:
HashMap
:尽力而为的失败快速迭代器。
clear()
Map:保证在下一个迭代器步骤中抛出ConcurrentModificationException
CopyOnWriteArrayList
:快照迭代器
clear()
列表不会停止迭代 ConcurrentSkipListMap
:弱一致的迭代器
clear()
地图可能会或可能不会停止迭代,删除条目可能会或可能不会阻止它们在剩余的迭代期间显示