我最近参加了一次面试,面试官问了我有关 HashMap
和 ConcurrentHashMap
的问题。在最初的线程安全优势之后,我说过与 HashMap
相比,它在遍历时不会抛出 ConcurrentModificationException
。
面试官继续问我为什么不扔。我说过,HashMap
内部有一个变量 modCount
,它保存对映射所做的修改数量,而 Iterator
将此数字与其实际值进行比较在创建时已初始化。如果此数字不同,则会引发异常,并且 ConcurrentHashMap 不会发生此比较。
然后他说 ConcurrentHashMap
在遍历时会复制自身。
我对这个说法表示怀疑,因为以前没有遇到过这种情况。即使阅读文档也没有给我足够的答案。
它会在任何时候制作副本(读或写)吗?
最佳答案
不,它与复制任何东西无关。并且在多线程环境下不一定会抛出ConcurrentModificationException
。
关于您的问题的几点。
ConcurrentModificationException
与并发性无关(而且它实际上有一个坏名字)。当您迭代某些数据结构并且在对该结构进行一些修改后再次使用迭代器时,会抛出该错误。这称为快速失败
行为。由于这种行为,它可以在多线程上下文中抛出,但对此完全没有依赖性。ConcurrentHashMap
是一个精致的结构,我建议你仔细阅读它。它实现并发的方式是通过任何访问映射中不同存储桶的线程都是自然分离的,因此除了重新哈希情况之外不需要在它们之间进行同步。
对于访问同一个存储桶的线程,如果存储桶为空,则使用 CAS,即
AtomicReference
来访问存储桶中的唯一元素。如果操作中的桶发生哈希冲突且有多个元素,则将其保留为链表(JDK 1.8 的情况除外,它将链表重新改造为二叉树,以在极端哈希下获得更好的性能碰撞)。链表的好处就是只要用CAS方式操作next指针就天然支持并发。
ConcurrentHashMap 中还有很多编码艺术,因此我建议您仔细阅读其源代码。
关于java - ConcurrentHashMap 是否会复制自身?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34039366/