一次 java.util.ConcurrentModificationException 问题的fix
其他写的比较好的文章:https://www.cnblogs.com/snowater/p/8024776.html
我在一次多线程读写map的时候,然后再遍历的时候也遇到了该问题。
现场代码
private ConcurrentHashMap<Long, Set<Long>> m = new ConcurrentHashMap<>();
// 多线程运行中
public void test(Long p, Long value) {
Set<Long> s = new HashSet<>();
if (m.contains(p)) {
s = m.get(p);
s.add(value);
} else {
s.add(value);
}
m.put(p, s);
for (Long id: s) {
logger.info("" + id);
}
}
可以看到,我是在多线程的读写一个线程安全的Map,但我用一个Set
分析
我们先看看这个错误是什么,下面是源码
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount;
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
这个是源代码,我们可以看到里面有个modCount来表示修改次数,每次对HashMap的操作都会增加modCount,如果在遍历的时候,发现当前的modCount和遍历的modCount不一致的时候,就会报错。
在我遇到的场景,Set的底层实际上就是用的Map的Key来做实现的,我的Set并不是一个线程安全的,而且还是一个浅拷贝(指向同一个地址),所以在多线程遍历Set的时候,会出现modCount不一致的问题,导致报错。
解决办法
因为避免不了浅拷贝,所以我的解决办法是将set替换成线程安全的,例如 ConcurrentHashMap,也可以是线程安全的Collection。
其他情况的解决办法
将报错的容器替换成线程安全的,例如万能的 ConcurrentHashMap;关注任何map的修改操作,可以试着将修改操作加锁。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2016-11-26 Codeforces Round #381 (Div. 1) B. Alyona and a tree dfs序 二分 前缀和
2016-11-26 uestc_retarded 模板
2016-11-26 CROC 2016 - Final Round [Private, For Onsite Finalists Only] C. Binary Table FWT
2015-11-26 SPOJ LIS2 Another Longest Increasing Subsequence Problem 三维偏序最长链 CDQ分治
2015-11-26 hdu 4494 Teamwork 最小费用最大流
2015-11-26 cdoj 574 High-level ancients dfs序+线段树