concurrenthashmap迭代器采用弱一致性遍历,不抛异常也不复制数据,通过读取volatile引用、跳过val为null或hash标记为moved的节点来容忍并发修改。

ConcurrentHashMap 的 EntryIterator 并不基于 fail-safe 机制,而是采用弱一致性(weakly consistent)遍历——它既不是 fail-fast,也不是传统意义上的 fail-safe。
ConcurrentHashMap 迭代器不抛异常,也不复制数据
不同于 CopyOnWriteArrayList 的“快照式” fail-safe(底层复制整个数组),ConcurrentHashMap 的迭代器在构造时不拷贝哈希表状态,也不冻结结构。它直接遍历当前 segment(JDK 7)或 Node 数组(JDK 8+),但通过以下方式容忍并发修改:
- 遍历时读取 volatile 的 next 引用和 table 数组,确保看到已完成的插入/删除操作;
- 删除节点时(如调用
remove()),仅将对应 Node 的val和next设为 null 或特殊标记(如MOVED、RESERVED),不改变正在遍历的链表/红黑树物理结构; - 迭代器遇到已被删除的节点(如 val == null 或 hash == MOVED),会跳过该节点,继续沿 next 向后推进。
EntryIterator 如何感知并跳过已删除节点
以 JDK 8 为例,EntryIterator 内部使用 Traverser 遍历,关键逻辑包括:
- 每次调用
next()前,先检查当前节点是否有效:若e.val == null(被删除)或e.hash (如 <code>MOVED或RESERVED),则跳过; - 跳过时,从当前桶的头节点重新开始扫描,或推进到下一个非空桶;
- 即使某次遍历中节点被并发删除,迭代器也不会卡住或报错,只是可能遗漏刚被删的元素**(不保证实时性),也不会重复返回**(无重复)。
与真正 fail-safe 的本质区别
fail-safe 通常指“遍历基于不可变快照”,而 ConcurrentHashMap 的设计目标是低延迟 + 高并发 + 内存友好:
- 不复制数据 → 无额外内存开销;
- 允许并发修改 → 不阻塞写线程;
- 弱一致性语义 → 不保证看到所有已提交修改,也不保证不看到未提交修改,但保证遍历过程安全、不抛异常、不无限循环。
实际编码中需注意
开发者不能依赖迭代器看到“最新全量视图”:
- 遍历过程中插入的新键值对,可能被看到,也可能看不到(取决于是否落到当前已扫描的桶);
- 遍历过程中删除的条目,大概率被跳过,但若删除发生在迭代器尚未到达该桶时,仍可能返回(因删除前已读取到该节点);
- 若业务强依赖一致性快照(如导出全量状态),应显式调用
entrySet().toArray()或手动加锁,而非依赖迭代器。
不复杂但容易忽略:它的“安全”来自精细的 CAS 控制和 volatile 协作,而不是隔离副本。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











