跳转至

CopyOnWriteArrayList:弱一致性与读并发

一、什么是弱一致性

CopyOnWriteArrayList 的读操作不保证看到最新写入,但保证最终一致。

list.add(1);
list.add(2);
new Thread(() -> list.add(3)).start();
list.get(2);   // 可能是 3,也可能抛 IndexOutOfBoundsException

写操作正在复制数组时,读操作读到的还是旧数组。

二、为什么读并发高

1. 读不加锁

get() 直接读 volatile 数组引用:

private transient volatile Object[] array;

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

没有 synchronized,没有 CAS,多个读线程同时访问。

2. 写不阻塞读

写操作复制新数组,旧数组不动。读线程手里的旧数组引用仍然有效,不会被修改。

三、迭代器的弱一致性

Iterator<Integer> it = list.iterator();
list.add(4);   // 写
while (it.hasNext()) {
    System.out.println(it.next());   // 看不到 4
}

迭代器创建时拍快照,后续写不可见。

四、为什么这样设计

  • 读多写少时,写操作少,复制成本可接受。
  • 读操作无锁,吞吐量高。
  • 不抛 ConcurrentModificationException,比 Vector/SynchronizedList 优雅。

五、和 Vector 对比

Vector CopyOnWriteArrayList
synchronized 加锁 无锁
synchronized 复制数组
迭代 可能 CME 快照,不 CME
适合 写多读少 读多写少

六、典型场景

  • 事件监听器列表:读多写少。
  • 配置白名单。
  • 缓存数据。

总结

CopyOnWriteArrayList 用"写时复制"换"读无锁",适合读多写少。代价是写贵、内存多、弱一致。