1. 概述

本文将深入探讨 java.util.concurrent 包中的 CopyOnWriteArrayList。这个并发集合类在多线程编程中非常实用,特别适合需要线程安全遍历列表且无需显式同步的场景。

2. CopyOnWriteArrayList API

CopyOnWriteArrayList 的设计采用了巧妙的"写时复制"机制实现线程安全:

  • 核心原理:调用 add()remove() 等修改方法时,会创建底层数组的全新副本
  • 遍历安全:迭代操作基于创建迭代器时的不可变快照,即使其他线程并发修改也不受影响
  • ⚠️ 性能权衡:修改操作需复制整个数组,适合读多写少的场景。频繁修改会导致性能下降
// 创建迭代器时获取数据快照
Iterator<String> iterator = list.iterator(); 
// 后续修改不影响已创建的迭代器
list.add("new element"); 

3. 在插入时迭代 CopyOnWriteArrayList

通过示例理解其行为:

CopyOnWriteArrayList<Integer> numbers 
  = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

// 创建迭代器(获取当前快照)
Iterator<Integer> iterator = numbers.iterator();

// 添加新元素(创建新数组副本)
numbers.add(10);

// 迭代快照中不包含新元素
List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);
assertThat(result).containsOnly(1, 3, 5, 8); // ✅ 不包含10

// 新迭代器包含所有元素
Iterator<Integer> iterator2 = numbers.iterator();
List<Integer> result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);
assertThat(result2).containsOnly(1, 3, 5, 8, 10); // ✅ 包含10

关键点:

  • 迭代器基于创建时刻的数据快照
  • 后续修改不影响已存在的迭代器
  • 新迭代器反映最新数据状态

4. 迭代时不允许删除

CopyOnWriteArrayList 的迭代器是只读的,尝试删除会抛出异常:

@Test(expected = UnsupportedOperationException.class)
public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() {
    
    CopyOnWriteArrayList<Integer> numbers
      = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});

    Iterator<Integer> iterator = numbers.iterator();
    while (iterator.hasNext()) {
        iterator.remove(); // ❌ 抛出 UnsupportedOperationException
    }
}

⚠️ 踩坑提醒:不要在迭代时尝试删除元素,这是设计限制而非 bug

5. 总结

CopyOnWriteArrayList 的核心价值在于:

  • 线程安全遍历:无需同步即可安全迭代
  • 弱一致性:迭代器反映创建时的数据状态
  • 写操作昂贵:每次修改都复制整个数组

适用场景:

  • 事件监听器管理
  • 配置信息存储
  • 读操作远多于写操作的场景

完整示例代码可在 GitHub 项目 中获取(Maven 项目,可直接导入运行)。


原始标题:Guide to CopyOnWriteArrayList | Baeldung

« 上一篇: Spring Roo
» 下一篇: JDBC 入门指南