导读:本期聚焦于小伙伴创作的《在Java中如何处理ConcurrentModificationException和线程安全问题》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《在Java中如何处理ConcurrentModificationException和线程安全问题》有用,将其分享出去将是对创作者最好的鼓励。

在Java集合框架的使用中,ConcurrentModificationException是开发者经常遇到的运行时异常,尤其在多线程操作集合或者单线程迭代集合时修改集合结构的场景下很容易触发,同时这类场景往往还伴随着线程安全的隐患,需要开发者掌握对应的处理方案。

在Java中如何处理ConcurrentModificationException和线程安全问题

ConcurrentModificationException的产生原因

ConcurrentModificationException的本质是集合的快速失败(fail-fast)机制导致的。大多数Java集合(如ArrayList、HashMap等)内部都维护了一个modCount变量,用来记录集合结构被修改的次数。当通过迭代器遍历集合时,迭代器会初始化一个expectedModCount变量,值等于当前集合的modCount

如果在迭代过程中,集合的结构被修改(比如添加、删除元素),modCount会自增,此时迭代器下次调用next()或者hasNext()方法时,会发现expectedModCountmodCount不一致,就会抛出ConcurrentModificationException。

单线程场景下的触发示例

在单线程中使用增强for循环遍历集合时直接修改集合结构,就会触发该异常:

import java.util.ArrayList;
import java.util.List;

public class SingleThreadConcurrentModification {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        // 增强for循环本质是迭代器遍历,遍历过程中删除元素会触发异常
        for (String item : list) {
            if ("b".equals(item)) {
                list.remove(item); // 直接修改集合结构,modCount变化,迭代器校验失败
            }
        }
    }
}

处理ConcurrentModificationException的常用技巧

1. 使用迭代器自身的删除方法

如果是单线程场景下的遍历删除操作,可以使用迭代器的remove()方法,该方法会同步更新expectedModCount的值,避免校验失败:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorRemoveDemo {
    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 item = iterator.next();
            if ("b".equals(item)) {
                iterator.remove(); // 迭代器自身的删除方法,不会触发异常
            }
        }
        System.out.println(list); // 输出[a, c]
    }
}

2. 遍历前复制集合副本

如果需要遍历集合的同时做较多修改操作,可以先复制一份集合副本,遍历副本的同时修改原集合,这样不会触发迭代器的校验逻辑:

import java.util.ArrayList;
import java.util.List;

public class CopyListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        // 复制副本进行遍历
        List<String> copyList = new ArrayList<>(list);
        for (String item : copyList) {
            if ("b".equals(item)) {
                list.remove(item); // 修改原集合,遍历的是副本,不会触发异常
            }
        }
        System.out.println(list); // 输出[a, c]
    }
}

3. 使用并发集合替代普通集合

如果是多线程场景下的集合操作,普通集合的快速失败机制无法避免异常,此时可以使用Java并发包提供的并发集合,比如CopyOnWriteArrayListConcurrentHashMap等,这些集合的设计避免了快速失败机制,同时天然支持线程安全。

集合操作的线程安全问题及解决方案

除了ConcurrentModificationException,多线程操作普通集合还会带来数据不一致、脏读等线程安全问题,需要根据场景选择合适的解决方案。

1. 使用同步包装类

Java集合框架提供了Collections工具类的同步包装方法,可以将普通集合包装成线程安全的集合,比如Collections.synchronizedList()Collections.synchronizedMap()等,这些包装后的集合所有操作都会加对象级别的锁,保证同一时间只有一个线程能修改集合。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SynchronizedListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 包装成线程安全的List
        List<String> synchronizedList = Collections.synchronizedList(list);
        // 多线程添加元素
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronizedList.add("t1-" + i);
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronizedList.add("t2-" + i);
            }
        });
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(synchronizedList.size()); // 输出20,不会出现数据丢失
    }
}

需要注意的是,使用同步包装类遍历集合时,仍然需要手动加锁,否则遍历过程中其他线程修改集合还是可能触发异常:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SynchronizedListIterateDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        List<String> synchronizedList = Collections.synchronizedList(list);
        synchronizedList.add("a");
        synchronizedList.add("b");
        // 遍历时需要手动加锁
        synchronized (synchronizedList) {
            for (String item : synchronizedList) {
                System.out.println(item);
            }
        }
    }
}

2. 使用并发集合类

同步包装类的锁粒度较粗,性能相对较低,高并发场景下更推荐使用并发包下的并发集合,比如CopyOnWriteArrayList适合读多写少的场景,写操作时会复制整个数组,读操作不需要加锁;ConcurrentHashMap采用分段锁或者CAS+synchronized的方式,并发性能远优于同步包装的HashMap。

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListDemo {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("a");
        list.add("b");
        // 多线程遍历和修改都不会触发ConcurrentModificationException
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                list.add("t1-" + i);
            }
        });
        Thread t2 = new Thread(() -> {
            for (String item : list) {
                System.out.println(item);
            }
        });
        t1.start();
        t2.start();
    }
}

3. 手动加锁控制并发

如果使用的是自定义集合或者需要更细粒度的锁控制,可以通过synchronized关键字或者ReentrantLock手动加锁,保证集合操作的原子性:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class ManualLockDemo {
    private final List<String> list = new ArrayList<>();
    private final ReentrantLock lock = new ReentrantLock();

    public void addItem(String item) {
        lock.lock();
        try {
            list.add(item);
        } finally {
            lock.unlock();
        }
    }

    public String getItem(int index) {
        lock.lock();
        try {
            return list.get(index);
        } finally {
            lock.unlock();
        }
    }
}

不同场景的方案选择建议

可以根据实际场景选择合适的处理方案:

  • 单线程遍历删除场景:优先使用迭代器的remove()方法,简单且高效。
  • 单线程遍历修改较多场景:可以复制集合副本再操作,避免异常。
  • 多线程低并发场景:可以使用同步包装类,实现简单。
  • 多线程高并发场景:优先选择对应的并发集合类,性能更好。
  • 需要自定义锁逻辑场景:手动使用synchronized或者ReentrantLock控制并发。

只要理解集合的快速失败机制原理,结合场景选择合适的处理方案,就能有效避免ConcurrentModificationException,同时保障集合操作的线程安全。

ConcurrentModificationExceptionJava并发线程安全集合操作修改时间:2026-07-13 03:27:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。