ConcurrentModificationException的根本原因是fail-fast机制检测到集合被非迭代器方式结构性修改,单线程下调用list.remove()等方法也会触发;必须用iterator.remove()安全删除,或改用CopyOnWriteArrayList等线程安全集合。
Java中Iterator抛ConcurrentModificationException,根本原因是快速失败(fail-fast)机制在检测到集合被意外修改时主动中断遍历,不是因为“多线程并发”本身,而是单线程下用非迭代器方式修改了正在遍历的集合——这是最容易被误解的一点。
每个支持迭代的集合(如ArrayList、HashMap)内部都有一个modCount(修改计数器),记录结构修改次数。迭代器创建时会把当时的modCount复制为自己的expectedModCount。每次调用next()或hasNext()前,都会检查两者是否一致。只要集合被add()、remove()、clear()等方法结构性修改,modCount就会加1,而迭代器的expectedModCount没变,校验失败就抛异常。
常见“单线程踩坑”场景:
for (String s : list),边调用list.remove(s)
iterator.hasNext()循环,但删除元素时用了list.remove(index)而非iterator.remove()
Iterator提供了安全删除方法:仅在调用过next()之后,立刻调用iterator.remove(),它会同步更新expectedModCount,避免异常。
示例:
Iteratorit = list.iterator(); while (it.hasNext()) { String s = it.next(); if ("target".equals(s)) { it.remove(); // ✅ 安全删除 } }
CopyOnWriteArrayList(适合读多写少)、ConcurrentHashMap。它们不依赖modCount,迭代器基于快照,不会抛该异常for (int i = list.size()-1; i >= 0; i--),删除不影响后续索引,但需注意业务逻辑是否允许乱序处理即使用了iterator.remove(),在多线程环境下仍不安全:迭代器本身不是线程安全的。两个线程同时遍历+修改,依然可能出错。此时必须:
synchronized(list)块内完成整个遍历+修改)CopyOnWriteArrayList,
其迭代器天然支持并发读)java.util.concurrent包提供的高级工具(如ConcurrentLinkedQueue配合poll()消费)基本上就这些。核心记住一点:ConcurrentModificationException是集合自我保护的信号,不是bug,而是提醒你——别绕过迭代器直接动集合结构。