CopyOnWriteArrayList:弱一致性与读并发¶
一、什么是弱一致性¶
CopyOnWriteArrayList 的读操作不保证看到最新写入,但保证最终一致。
list.add(1);
list.add(2);
new Thread(() -> list.add(3)).start();
list.get(2); // 可能是 3,也可能抛 IndexOutOfBoundsException
写操作正在复制数组时,读操作读到的还是旧数组。
二、为什么读并发高¶
1. 读不加锁¶
get() 直接读 volatile 数组引用:
没有 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 用"写时复制"换"读无锁",适合读多写少。代价是写贵、内存多、弱一致。