Java并发工具:CopyOnWriteArrayList

Java并发工具:CopyOnWriteArrayList

经验文章nimo972025-05-21 2:14:461A+A-

CopyOnWriteArrayList 是 Java 中 java.util.concurrent 包提供的一种线程安全的 List 实现。它特别适用于读多写少(Read-mostly)的并发场景,比如事件监听器列表、配置管理等。


核心思想

  • 写时复制(Copy-On-Write)

每当对集合进行修改操作(如添加、删除、替换元素)时,它会创建一个新的数组副本,并在新副本上执行修改操作,最后再将引用指向新的数组。

而读取操作(如 get, iterator 等)不会加锁,直接访问当前数组。


  • 线程安全实现

通过 volatile 关键字修饰底层数组,保证可见性;写操作通过锁(ReentrantLock)实现互斥。

每次写操作生成新数组,避免并发写冲突,但可能短暂存在多个数组副本。


特点

特性

描述

线程安全

所有操作都是线程安全的,不需要外部同步

写操作加锁

修改操作(add, set, remove)使用重入锁(ReentrantLock)保护

读不加锁

读操作无锁,性能高

最终一致性

迭代器或快照视图看到的是创建那一刻的数据状态

高开销写操作

每次写都要复制整个数组,适合写少读多的场景

不抛出
ConcurrentModificationException

因为迭代基于快照

常用方法示例

import java.util.concurrent.CopyOnWriteArrayList;

public class CpListExample {

    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        // 添加元素
        list.add("A");
        list.add("B");
        list.add("C");

        // 读取元素
        System.out.println(list.get(0));  // 输出 A

        // 删除元素
        list.remove("B");

        // 遍历(不会抛出 ConcurrentModificationException)
        for (String s : list) {
            System.out.println(s);
        }
    }
}


内部原理简析

1. 内部维护一个 volatile transient Object[] array; 来保存数据。

2. 每次写操作都会复制原数组到新数组并修改新数组。

3. 使用 ReentrantLock 来保证写操作的原子性和可见性。

4. 读操作直接访问 array,因为 volatile 能确保可见性。


适用场景

适合场景:

- 大量读操作,少量写操作

- 对实时一致性要求不高(最终一致即可)


不适合场景:

- 写操作频繁

- 数据量大(每次写都复制数组,性能差)


注意事项

  • 内存开销:频繁写操作会导致大量数组复制,可能引发内存和 GC 压力。
  • 实时性不足:读操作可能获取旧数据,不适合强一致性场景。
  • 迭代器限制:迭代器不支持修改操作(如 remove、set),调用会抛出 UnsupportedOperationException。


与 Vector / Collections.synchronizedList 对比

特性

Vector

Collections.synchronizedList

CopyOnWriteArrayList

线程安全

读写是否加锁

读写都加锁

读写都加锁

仅写加锁

并发性能

一般

好(读并发强)

是否抛 CME

是否支持快照遍历

点击这里复制本文地址 以上内容由nimo97整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

尼墨宝库 © All Rights Reserved.  蜀ICP备2024111239号-7