多线程场景下,如何在迭代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
相关产品推荐
相关产品推荐

