Java 集合遍历过程中修改数据触发 Fail-Fast 机制 ,导致报ConcurrentModificationException异常

Java Fail-Fast 机制

Fail-Fast 机制是 Java 集合框架中的一种错误检测机制,用于在遍历集合时检测结构修改。如果在迭代器创建之后,集合被修改(例如添加或删除元素),并且这种修改不是通过迭代器自身的 remove() 方法进行的,那么迭代器会立即抛出 ConcurrentModificationException 异常,以防止不一致或不可预测的行为。

工作原理
  1. 修改计数器

    • 集合类(如 ArrayListHashMap 等)内部维护一个 modCount 计数器,记录集合被结构性修改的次数(结构性修改包括添加或删除元素,但不包括通过迭代器自身的 remove() 方法进行的删除)。
  2. 迭代器的预期修改计数

    • 当创建迭代器时,迭代器会记录当前集合的 modCount 值,作为其 expectedModCount
    • 在每次调用迭代器的 next() 方法时,迭代器会检查 expectedModCount 是否与集合的当前 modCount 一致。
  3. 检测不一致

    • 如果在迭代过程中,集合的 modCount 发生变化(即 expectedModCount 不等于 modCount),迭代器会立即抛出 ConcurrentModificationException 异常。
示例代码
java 复制代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class FailFastExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
            // 在迭代过程中修改集合,会抛出 ConcurrentModificationException
            list.add("D");
        }
    }
}

输出

复制代码
A
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)
    at FailFastExample.main(FailFastExample.java:13)
解决方法
  1. 使用迭代器自身的 remove() 方法

    • 如果需要在遍历过程中删除元素,应使用迭代器的 remove() 方法,而不是直接操作集合。
    java 复制代码
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String element = iterator.next();
        if (element.equals("B")) {
            iterator.remove(); // 安全删除元素
        }
    }
  2. 使用线程安全的集合类

    • 使用 java.util.concurrent 包中的线程安全集合类,如 CopyOnWriteArrayListConcurrentHashMap 等。
    java 复制代码
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class ConcurrentExample {
        public static void main(String[] args) {
            CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
            list.add("A");
            list.add("B");
            list.add("C");
    
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
                // 在迭代过程中修改集合,不会抛出 ConcurrentModificationException
                list.add("D");
            }
        }
    }
  3. 使用 Collections.synchronizedList()Collections.synchronizedSet()

    • 将集合包装为线程安全的集合。
    java 复制代码
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    
    public class SynchronizedExample {
        public static void main(String[] args) {
            List<String> list = Collections.synchronizedList(new ArrayList<>());
            list.add("A");
            list.add("B");
            list.add("C");
    
            synchronized (list) {
                Iterator<String> iterator = list.iterator();
                while (iterator.hasNext()) {
                    String element = iterator.next();
                    System.out.println(element);
                    // 在迭代过程中修改集合,不会抛出 ConcurrentModificationException
                    list.add("D");
                }
            }
        }
    }
注意事项
  1. 单线程环境

    • 在单线程环境中,Fail-Fast 机制有助于及时发现集合被意外修改的问题。
    • 但需要注意在迭代过程中不要直接修改集合,除非使用迭代器自身的 remove() 方法。
  2. 多线程环境

    • Fail-Fast 机制在多线程环境中可能会导致 ConcurrentModificationException 异常。
    • 应使用线程安全的集合类或同步机制来避免此类问题。
  3. 性能影响

    • Fail-Fast 机制本身对性能的影响较小,主要体现在每次迭代时的 modCount 检查。
    • 但在多线程环境下,频繁的同步操作可能会显著影响性能。
总结
  • Fail-Fast 机制 是 Java 集合框架中用于检测集合在迭代过程中被修改的一种机制。
  • 通过在迭代过程中抛出 ConcurrentModificationException 异常,Fail-Fast 机制可以及时发现不一致的行为,确保集合的完整性和一致性。
  • 在使用 Fail-Fast 机制时,需要注意在迭代过程中不要直接修改集合,除非使用迭代器自身的 remove() 方法。
  • 对于多线程环境,建议使用线程安全的集合类或同步机制来避免 ConcurrentModificationException 异常。

通过合理使用 Fail-Fast 机制,可以提高代码的健壮性和可靠性。

相关推荐
deepxuan几秒前
Day7--python
开发语言·python
曲幽10 分钟前
FastAPI不止于API:手把手教你用Jinja2打造动态Web页面
python·fastapi·backend·jinja2·full stack·template engine·web development
小小小米粒14 分钟前
Maven Tools
java
禹凕15 分钟前
Python编程——进阶知识(多线程)
开发语言·爬虫·python
Ulyanov18 分钟前
基于Pymunk物理引擎的2D坦克对战游戏开发
python·游戏·pygame·pymunk
铉铉这波能秀19 分钟前
LeetCode Hot100数据结构背景知识之字典(Dictionary)Python2026新版
数据结构·python·算法·leetcode·字典·dictionary
kali-Myon29 分钟前
2025春秋杯网络安全联赛冬季赛-day1
java·sql·安全·web安全·ai·php·web
我是咸鱼不闲呀32 分钟前
力扣Hot100系列20(Java)——[动态规划]总结(下)( 单词拆分,最大递增子序列,乘积最大子数组 ,分割等和子集,最长有效括号)
java·leetcode·动态规划
程序媛徐师姐35 分钟前
Python基于爬虫的网络小说数据分析系统【附源码、文档说明】
爬虫·python·python爬虫·网络小说数据分析系统·pytho网络小说数据分析系统·python爬虫网络小说·python爬虫的网络小说数据
清水白石00842 分钟前
深入解析 LRU 缓存:从 `@lru_cache` 到手动实现的完整指南
java·python·spring·缓存