concurrenthashmap的clear()方法是弱一致性的:它逐段清空、不加全局锁,允许并发写入,故执行后map中仍可能存在数据;迭代器不抛concurrentmodificationexception,但可能漏读、提前结束或看到部分清空状态。

ConcurrentHashMap 的 clear() 方法本身不阻塞迭代器,而它的迭代器在 clear 过程中表现出来的行为,正是其“弱一致性”(weakly consistent)而非真正 fail-safe 的直接体现。
它不会抛异常,但也不提供快照语义或强一致性保证——这点常被误读为“fail-safe”,实则设计目标不同。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
ConcurrentHashMap 的 clear() 是怎么工作的?
-
clear()并非原子操作,而是逐段(JDK 8+ 是逐 bin / table 桶)清空; - 它不加全局锁,只对每个待清空的桶加锁(或用 CAS 清空单个 Node);
- 其他线程仍可并发写入未被清空的桶,甚至已清空的桶也可能被新写入(因
clear不冻结结构); -
clear()执行期间,size()可能返回近似值,isEmpty()也不一定立刻返回true。
迭代器在 clear 过程中的实际表现
- 迭代器构造时只记录当前 table 引用和起始桶索引,之后遍历依赖 volatile 读取链表/红黑树节点;
- 若某个桶在迭代器尚未访问时已被
clear()清空:该桶内容将完全不可见,迭代器跳过; - 若某个桶刚被迭代器读完头节点,紧接着被
clear()清空后续节点:迭代器可能停在已断开的链上,提前结束该桶遍历; - 若
clear()和迭代器同时访问同一桶:由于 Node.next 和 val 是 volatile,迭代器可能看到部分清空状态(如 key 存在但 value 为 null),也可能跳过整个节点; -
绝不会抛
ConcurrentModificationException—— 因为没有modCount机制,也无修改检测逻辑。
与真正 fail-safe 集合(如 CopyOnWriteArrayList)的关键区别
- ✅ CopyOnWriteArrayList:
iterator()返回的是构造时刻数组的不可变副本,clear()影响的是新数组,旧副本完全不受干扰 → 迭代器一定看到初始全部元素; - ❌ ConcurrentHashMap:迭代器始终读原始结构,只是靠 volatile + 分段更新容忍并发 → 迭代结果可能漏、可能不全、可能滞后,但不断、不崩。
实际编码中要注意什么?
- 不要假设
keySet().iterator()能遍历到clear()开始前就存在的所有键; - 不要用迭代过程中的
map.size()或map.isEmpty()做流程判断依据; - 若业务要求“遍历前先冻结状态”,应自行加锁或改用
Collections.unmodifiableMap(new HashMap(map))快照; -
clear()期间插入的新键,是否被迭代器看到不确定,取决于插入位置与迭代进度是否重叠。
本质上,ConcurrentHashMap 的设计选择是:吞吐优先,一致性让步。它不是 fail-safe,而是“不失败、不保证、不承诺”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










