You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Collections同步List仍抛出ConcurrentModificationException问题排查

Collections.synchronizedList转同步List后仍抛ConcurrentModificationException的原因及解决

问题场景

将普通ArrayList通过Collections.synchronizedList转为同步List后,多线程下执行迭代+修改操作,依然抛出ConcurrentModificationException异常,代码如下:

package Concurrent_Collections;

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

public class DemoForConcurrentModificationException extends Thread {

    static List<Integer> myList = new ArrayList<Integer>();
    static List<Integer> synchronizedList;

    public static void main(String[] args) {
        System.out.println("Main Thread");
        synchronizedList = Collections.synchronizedList(myList);
        for (int i = 0; i < 10; i++) {
            synchronizedList.add(i);
        }

        DemoForConcurrentModificationException t = new DemoForConcurrentModificationException();
        t.start();
        for (Integer k : synchronizedList) {
            System.out.println("Main");
            System.out.println(k);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void run() {
        System.out.println("Child Thread");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronizedList.add(100);
    }
}

原因分析

Collections.synchronizedList仅为List的单个方法调用添加同步锁,保证单个add、get等操作的线程安全性,但迭代操作(比如增强for循环)属于复合操作:它会先获取迭代器,之后循环执行hasNext()和next(),这些步骤之间没有被整体加锁。

在你的代码中:

  • 主线程用增强for循环遍历List,每次迭代间隔1秒
  • 子线程在2秒后调用add()修改List结构
  • 主线程的迭代器会检测到List的modCount(修改次数)和迭代器自身的expectedModCount不一致,触发快速失败机制,抛出ConcurrentModificationException

解决办法

办法一:迭代时手动加锁

synchronizedList本身可作为锁对象,在迭代的整个过程中加锁,保证迭代和其他修改操作互斥:

修改主线程的遍历代码:

synchronized(synchronizedList) {
    for (Integer k : synchronizedList) {
        System.out.println("Main");
        System.out.println(k);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

办法二:使用CopyOnWriteArrayList

CopyOnWriteArrayList是Java并发包专为并发迭代设计的List实现,它的修改操作(add、remove等)会复制一份新的底层数组,迭代操作基于旧数组的快照进行,不会受后续修改影响,自然不会抛出ConcurrentModificationException。

替换原有的同步List定义:

// 直接替换成CopyOnWriteArrayList,无需用Collections.synchronizedList包装
static List<Integer> synchronizedList = new CopyOnWriteArrayList<>();

内容的提问来源于stack exchange,提问作者Nitesh Agrawal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:45:32