CopyOnWriteArrayList能实现无锁读取,因其内部持volatile数组引用,读操作直接访问当前快照,写操作则加锁复制数组并原子更新引用;适用于读远多于写、允许弱一致性的场景。

CopyOnWriteArrayList 适合读多写少场景,核心在于“读不加锁、写时复制”,让读操作几乎零开销,写操作虽有复制成本但可控。
为什么它能实现无锁读取
CopyOnWriteArrayList 内部持有一个 volatile 数组引用。每次读操作(如 get()、size()、iterator())都直接访问当前数组快照,不加任何同步;写操作(如 add()、remove())则先复制整个数组,在新数组上修改,再用 CAS 原子更新引用。读线程永远看到某个一致的快照,天然线程安全,无需 synchronized 或 ReentrantLock。
适用边界:真·读多写少
写操作代价是 O(n) 时间 + 额外内存(复制数组),所以必须满足:
• 读操作频次远高于写操作(例如每秒千次读、几秒一次写)
• 列表元素数量适中(几百到几千较稳妥;若达数万,单次 add 可能引发明显暂停)
• 允许读取结果短暂滞后——写操作完成后,新元素对正在迭代的 Iterator 不可见(弱一致性)
典型用法与避坑点
推荐用法:
• 缓存监听器列表(如事件总线中的 Listener 集合)
• 配置项白名单、运行时开关集合等低频变更的只读查询场景
• 替代 Vector 或 Collections.synchronizedList 做纯读密集型访问
必须避免:
• 在 for-each 循环中调用 remove() —— 迭代器不支持结构性修改
• 用普通 for 循环反复 get(i) 并依赖实时 size() —— size() 返回的是快照长度,写操作期间可能已变
• 把它当普通 ArrayList 做高频增删 —— 复制开销会拖垮性能
简单优化技巧
• 初始化时预估容量(new CopyOnWriteArrayList(initialCapacity)),减少早期扩容复制次数
• 批量写入优先用 addAll() 而非多次 add(),可减少复制次数(内部仍是一次复制)
• 若需强一致性读写(如写后立刻被所有读感知),它不适合——考虑读写锁或并发容器组合方案

















