跳转至

CopyOnWriteArrayList:写时复制

一、是什么

线程安全的 List,读操作无锁,写操作复制整个数组。

List<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
list.get(0);

二、原理

直接读内部数组:

private transient volatile Object[] array;

public E get(int index) {
    return get(array, index);
}

不加锁。

复制一份新数组,改完再替换引用:

public boolean add(E e) {
    synchronized (lock) {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    }
}

为什么安全

  • 写时 synchronized 保证只有一个线程在复制。
  • 写完成后,新数组通过 volatile 引用替换,读线程立即看到。
  • 读线程手里可能拿着旧数组,但读操作不受影响(不会 ConcurrentModificationException)。

三、优缺点

优点

  • 读操作完全无锁,极高并发读。
  • 迭代器不会抛 ConcurrentModificationException。

缺点

  • 写操作贵:每次都复制整个数组。
  • 内存占用:同时存在旧数组和新数组。
  • 数据弱一致性:读可能读到旧值。

四、适用场景

  • 读多写少:配置、监听器、白名单。
  • 不适合写多的场景。

五、和 ArrayList 对比

ArrayList CopyOnWriteArrayList
线程安全
读性能 快(无锁)
写性能 快(尾插均摊 O(1)) 慢(O(n))
迭代 可能 CME 不会 CME
内存 多(双数组)

注意

CopyOnWriteArrayList 的迭代器是快照,不会反映写后的变化。这就是"弱一致性"。