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

Java Synchronized Collections为何抛ConcurrentModificationException及适用场景

Synchronized Collections 适用场景及问题解析

首先给出触发异常的代码:

import java.util.*;
public class SynchFail {
    static List<Integer> LIST = new ArrayList<Integer>();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    LIST.add(1);
                }
            }}).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    List<Integer> syncList = Collections.synchronizedList(LIST);
                    synchronized(syncList) {
                        for (Integer thisInt : syncList) {
                        }
                    }
                }
            }}).start();
    }
}

运行正常的代码:

import java.util.*;
public class SynchSucceed {
    static List<Integer> LIST = new ArrayList<Integer>();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized(LIST) {
                        LIST.add(1);
                    }
                }
            }}).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized(LIST) {
                        for (Integer thisInt : LIST) {
                        }
                    }
                }
            }}).start();
    }
}

异常原因说明

ConcurrentModificationException的触发根源是读写线程的同步规则不一致:

  • 第一段代码的写线程直接操作原始ArrayList的add方法,无任何同步控制,随时会修改集合的modCount计数
  • 读线程临时包装出的synchronizedList实例的锁对写线程完全无效,写线程根本不会感知这把锁的存在,迭代过程中集合被修改就会触发fail-fast机制抛出异常
    第二段代码所有读写操作都用同一把锁控制LIST的访问,所以不会出现迭代中途集合被修改的情况,运行正常。

Synchronized Collections 核心特性

Collections.synchronizedXXX系列包装的同步集合,仅保证单个方法(如add()/remove()/get())调用的原子性,对于迭代、批量操作、条件更新这类复合操作,不会自动为整个操作流程加锁,需要手动控制同步。

适用场景

  • 业务以单个原子读写操作为主的场景:如果很少需要迭代、批量更新等复合操作,用同步集合可以避免手动给每个单操作加锁,代码更简洁
  • 所有线程仅访问包装后同步集合实例的场景:确保没有任何线程直接操作被包装的原始集合,所有读写都走同一个同步集合实例,其内置锁才能对所有操作生效
  • 并发量不高、对性能要求宽松的场景:同步集合用的是独占全量锁,读写互斥,并发度低。如果并发量不大,用它的开发成本比CopyOnWriteArrayList、ConcurrentHashMap这类并发容器更低,逻辑也更简单
  • 复合操作可主动手动加锁的场景:如果需要做迭代、“不存在则添加”这类复合操作,主动给同步集合实例加锁即可保证整个操作的原子性

注意事项

  • 禁止同时操作包装后的同步集合和原始集合,否则锁规则会直接失效
  • 迭代操作必须手动加锁,同步集合不会自动为迭代流程加锁,不加锁依然有ConcurrentModificationException风险
  • 所有读写线程必须遵守同一套锁规则,不能出现部分线程加锁、部分线程不加锁的情况

内容的提问来源于stack exchange,提问作者Dave Carpeneto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 17:36:03