ConcurrentHashMap 迭代器不检查 modCount 且无锁遍历,故不会抛 ConcurrentModificationException;它提供弱一致性快照,适合读多写少场景。

直接用 ConcurrentHashMap 替换普通 HashMap,并接受其迭代器“不保证看到最新修改”的特性,就能自然避开 ConcurrentModificationException。这不是妥协,而是明确的设计取舍——用弱一致性换高并发吞吐和无锁遍历。
为什么弱一致性能避免 ConcurrentModificationException
普通集合(如 HashMap)的迭代器依赖 modCount 字段做“快速失败”校验:一旦发现结构被修改,立刻抛异常。而 ConcurrentHashMap 的迭代器完全不检查 modCount,也不加锁、不阻塞写操作。它基于分段哈希表的 volatile 读和非原子切换逻辑完成遍历,因此:
- 即使你一边
put()一边iterator().next(),也不会触发异常 - 遍历结果反映的是迭代开始时某一时刻的“尽力而为”快照,不是实时视图
- 新增元素可能漏读,扩容中节点可能重复,但已存在的元素不会消失
实战中怎么安全使用它的迭代器
关键不是“让它变强”,而是按它的行为边界来编码。常见误操作是把弱一致性当成“最终一致”或“线性一致”来用。
- 不要在循环体内依赖刚插入的 key 是否已被遍历到(它大概率没被看到)
- 避免对迭代结果做“必须完整覆盖所有当前数据”的业务判断(比如统计总数、校验全量状态)
- 若需强一致性快照,手动先调用
new ConcurrentHashMap(map)或map.entrySet().toArray()获取副本再遍历 - 删除操作仍建议用
Iterator.remove()(它支持),而不是map.remove(key)(虽不抛异常,但语义易混淆)
对比其他方案的适用场景
弱一致性不是万能解,要结合读写比例和延迟容忍度选型:
- ConcurrentHashMap:适合读多写少、允许漏读少量新数据的缓存、配置映射、会话索引等场景
- CopyOnWriteArrayList:适合监听器列表、白名单等极少修改、大量只读遍历的场景;写操作复制整个数组,别用于大集合或高频更新
- Collections.synchronizedMap:仅当并发度极低、且必须与旧代码兼容时考虑;遍历时全局锁,性能瓶颈明显
- 显式加锁 + 普通 HashMap:仅限需要强一致性且能接受串行化遍历的极少数控制流逻辑
一个典型安全写法示例
以下代码在多线程环境下稳定运行,不抛异常,也符合弱一致性预期:
ConcurrentHashMap<string integer> cache = new ConcurrentHashMap();
cache.put("A", 1);
cache.put("B", 2);
// 线程1:持续写入
new Thread(() -> {
for (int i = 0; i {
for (Map.Entry<string integer> entry : cache.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
// 即使这里 cache.put("X", 999),也不会中断遍历
}
}).start();
</string></string>










