3分钟解锁加密音乐:打破平台限制,让音乐真正属于你
2026/6/3 11:53:02
CopyOnWriteArrayList是一个线程安全的 List,它通过“写时复制”(Copy-On-Write)策略实现并发控制:所有修改操作(add/set/remove 等)都会创建底层数组的一个全新副本,而读操作(get/iterator/size 等)直接访问当前快照,无需加锁。
volatile的,读操作看到的是某个“瞬间快照”,天然线程安全。ReentrantLock保证同一时间只有一个写操作。Arrays.copyOf)array引用原子地指向新数组(volatile写)publicbooleanadd(Ee){finalReentrantLocklock=this.lock;lock.lock();try{Object[]elements=getArray();// 旧数组Object[]newElements=Arrays.copyOf(elements,len+1);// 复制newElements[len]=e;// 修改副本setArray(newElements);// volatile 写,切换引用returntrue;}finally{lock.unlock();}}iterator()时,会捕获当前array的引用(快照)。ConcurrentModificationExceptionremove()/set()/add()→ 抛UnsupportedOperationExceptionpublicIterator<E>iterator(){returnnewCOWIterator<E>(getArray(),0);// 传入当前 array 快照}✅读多写少(如监听器列表、配置项缓存)
✅遍历时不能加锁(避免死锁或性能瓶颈)
❌写操作频繁→ 每次复制数组开销大(O(n) 时间 + 内存)
❌实时性要求高→ 迭代器看不到最新修改
| 特性 | 说明 |
|---|---|
| 线程安全 | 所有可变操作加锁,读操作无锁但可见性由volatile保证 |
| 内存一致性 | happens-before:写入前的操作对后续读取该元素的线程可见 |
| 允许 null 元素 | null被当作普通元素处理 |
| fail-safe 迭代器 | 基于快照,不抛ConcurrentModificationException |
| 高写成本 | 每次写都复制整个数组,适合小规模、低频修改 |
| 最终一致性 | 读操作可能看到“过期”数据,但不会出错 |
Vector/Collections.synchronizedList对比CopyOnWriteArrayList | Vector/synchronizedList | |
|---|---|---|
| 读性能 | ⭐⭐⭐ 极高(无锁) | ⭐ 需要获取锁 |
| 写性能 | ⭐ 极低(复制整个数组) | ⭐⭐ 中等(只锁方法) |
| 迭代行为 | 快照,不阻塞写,不抛异常 | 需手动同步,否则可能抛ConcurrentModificationException |
| 内存占用 | 高(写时双倍内存) | 低 |
// 典型场景:事件监听器列表publicclassEventBus{privatefinalList<Listener>listeners=newCopyOnWriteArrayList<>();publicvoidaddListener(Listenerl){listeners.add(l);// 安全}publicvoidfireEvent(Evente){// 遍历时其他线程可安全增删监听器for(Listenerl:listeners){l.onEvent(e);}}}iterator()时的状态。if (!list.contains(x)) list.add(x)不是原子的,需外部同步。volatile Object[] array:保证数组引用的可见性。ReentrantLock lock:细粒度控制写操作。COWIterator:实现快照语义。addIfAbsent/addAllAbsent:提供“去重添加”语义,内部也基于快照判断。CopyOnWriteArrayList是为高并发读、低频写场景量身定制的线程安全容器。它用空间换时间 + 最终一致性的思路,巧妙避免了读写冲突,是并发编程中“乐观锁”思想的经典体现。
如果你正在处理类似“观察者列表”、“白名单配置”等场景,它往往是比synchronizedList更优的选择。