循环中出现concurrentmodificationexception源于fail-fast机制而非真并发;应按场景选安全集合:读多写少用copyonwritearraylist,map场景首选concurrenthashmap,慎用collections.synchronizedlist,复杂逻辑宜先收集待删元素再统一删除。

循环中出现 ConcurrentModificationException,不是因为“真并发”,而是集合的 fail-fast 机制检测到结构被意外修改。用安全集合处理,关键在于选对类型、用对场景,而不是一概套用。
优先选 CopyOnWriteArrayList(读多写少)
它允许在遍历时安全调用 add()、remove()、set(),底层是写时复制——每次修改都新建数组,原迭代器继续读旧快照,自然不会触发异常。
- 适合监听器列表、白名单、配置项等读远多于写的场景
- 遍历时删元素不会报错,但新添加的元素本次循环不可见
- 写操作开销大,频繁增删会导致内存飙升和 GC 压力,不适用于高频更新的业务列表
Map 场景首选 ConcurrentHashMap
它不是“完全 fail-safe”,但设计上规避了常见陷阱:其 keySet()、values()、entrySet() 返回的视图在迭代时不会抛 CME,且支持并发读写。
- 遍历
map.entrySet()时可安全调用map.remove(key) - 避免用
map.keySet().iterator()配合map.remove()—— 这仍可能出问题,应改用computeIfAbsent或replace等原子方法 - 不支持
Iterator.remove(),但提供了更安全的替代方案,如remove(key, value)校验删除
别踩 Collections.synchronizedList 的坑
它只是给每个方法加了 synchronized,表面线程安全,但遍历本身不是原子操作。
- 若用
for-each遍历它,依然会抛 CME —— 因为迭代器内部没同步预期状态 - 真要用,必须手动同步整个遍历块:
synchronized(list) { for (x : list) { ... } } - 性能差、易遗漏、代码臃肿,不如直接换
CopyOnWriteArrayList或重构逻辑
复杂判断逻辑?先收集再统一删
当条件涉及多个字段、外部依赖或需跨元素比较时,removeIf() 或并发集合难以覆盖,这时最稳妥的做法是:
- 新建一个
HashSet(去重快)或ArrayList(保持顺序)暂存待删对象 - 遍历原集合只做判断,不修改任何内容
- 遍历结束后调用
list.removeAll(toRemove),或逐个remove() - 若原集合是
HashSet,用HashSet存待删项,removeAll效率接近 O(n)










