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

多线程场景下,如何在迭代List等数据结构时对其加锁?

解决ArrayList多线程迭代与修改的并发问题

你的问题核心在于直接暴露了内部可变ArrayList的引用,导致多线程无同步地同时迭代和修改集合,触发ConcurrentModificationException或数据不一致。以下是几种无需使用CopyOnWriteArrayList的解决方案:


1. 封装所有集合操作,不暴露原引用

最根本的解决方式是禁止外部直接获取集合引用,将遍历、修改等操作全部封装到类A内部,由类A统一控制同步逻辑。

示例代码:

class A {
    private List<Integer> list = new ArrayList<>();
    // 用独立锁对象替代方法级锁,减少锁粒度影响
    private final Object lock = new Object();

    public A() {
        // 初始化list
    }

    // 封装遍历操作
    public void iterateList(Consumer<Integer> consumer) {
        synchronized (lock) {
            list.forEach(consumer);
        }
    }

    // 封装删除操作
    public void removeElements(Predicate<Integer> predicate) {
        synchronized (lock) {
            list.removeIf(predicate);
        }
    }
}

调用方使用方式:

public static void main(String[] args) {
    A a = new A();

    Thread t1 = new Thread(() -> {
        a.iterateList(System.out::println);
    });
    Thread t2 = new Thread(() -> {
        a.removeElements(e -> e == 1);
    });

    t1.start();
    t2.start();
}

这种方式保证了同一时间只有一个线程能操作集合,彻底避免并发冲突,且性能开销远低于CopyOnWriteArrayList。


2. 返回不可变视图+封装修改接口

如果必须让外部获取集合的"只读视图",可以返回Collections.unmodifiableList()包装后的不可变列表,同时所有修改操作仍由类A内部加锁执行。

示例代码:

class A {
    private List<Integer> list = new ArrayList<>();
    private final Object lock = new Object();

    public A() {
        // 初始化list
    }

    // 返回不可变视图,外部无法修改原集合
    public List<Integer> getList() {
        synchronized (lock) {
            // 返回原集合的快照拷贝,避免遍历过程中原集合修改引发异常
            return Collections.unmodifiableList(new ArrayList<>(list));
        }
    }

    // 封装修改操作,加锁保证线程安全
    public void removeElements(Predicate<Integer> predicate) {
        synchronized (lock) {
            list.removeIf(predicate);
        }
    }
}

这种方式兼顾了外部读取需求和内部修改的线程安全,快照拷贝的性能开销远低于CopyOnWriteArrayList的全量拷贝。


3. 使用显式锁替代synchronized(灵活场景)

如果需要更灵活的锁控制(如超时获取锁、可中断锁),可以用ReentrantLock替代synchronized,核心逻辑和第一种方案一致。

示例代码:

class A {
    private List<Integer> list = new ArrayList<>();
    private final ReentrantLock lock = new ReentrantLock();

    public A() {
        // 初始化list
    }

    public void iterateList(Consumer<Integer> consumer) {
        lock.lock();
        try {
            list.forEach(consumer);
        } finally {
            lock.unlock();
        }
    }

    public void removeElements(Predicate<Integer> predicate) {
        lock.lock();
        try {
            list.removeIf(predicate);
        } finally {
            lock.unlock();
        }
    }
}

内容的提问来源于stack exchange,提问作者Kfir Ettinger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:03:53