copyonwritearrayset适合读多写少场景,因其读操作无锁高性能,写操作通过复制数组实现线程安全,但开销大、内存高、不支持null且弱一致性。

CopyOnWriteArraySet 是 Java 并发包(java.util.concurrent)中一个线程安全的 Set 实现,底层基于 CopyOnWriteArrayList 构建。它特别适合“读多写少”的并发场景——即多个线程频繁读取、极少修改集合内容的使用模式。
为什么 CopyOnWriteArraySet 适合读多写少?
它的核心机制是“写时复制”(Copy-On-Write):每次添加、删除或更新元素时,并不直接修改原数组,而是先复制一份新数组,在新数组上完成操作,再用原子方式替换原引用。这意味着:
- 读操作(如
contains()、iterator()、size())完全无锁,也不阻塞其他读线程,性能极高; - 写操作(如
add()、remove())开销较大——涉及数组复制、对象创建和 volatile 引用更新,且会阻塞其他写操作; - 迭代器是快照式的,不会抛出
ConcurrentModificationException,但看不到写操作的最新结果(弱一致性)。
典型适用场景举例
以下情况可优先考虑 CopyOnWriteArraySet:
- 监听器/观察者列表:例如事件总线中注册的监听器集合,通常初始化后很少增删,但每次事件触发都要遍历通知;
- 配置白名单或黑名单:如 IP 白名单集合,加载后长期只读,仅运维偶尔动态更新;
- 缓存中的活跃会话 ID 集合:大量查询是否存在某会话,新增/下线频率远低于查询频率。
使用时需注意的关键细节
它不是万能替代品,误用反而降低性能或引发逻辑问题:
-
不支持 null 元素:调用
add(null)会直接抛出NullPointerException; - 不保证实时可见性:写操作完成后,正在执行的迭代器仍遍历旧快照,新线程读取才可见;
- 内存占用较高:每次写操作都复制整个底层数组,集合大、写频繁时易引发 GC 压力;
-
不支持批量写入优化:如
addAll()仍是逐个 add,无法合并为一次复制。
对比其他线程安全 Set 的选择建议
若场景不符合“读远多于写”,应考虑更合适的方案:
- 读写均衡或写较多 → 用
Collections.synchronizedSet(new HashSet())或ConcurrentHashMap.newKeySet()(JDK 8+); - 需要强一致性 + 高并发写 → 优先选
ConcurrentSkipListSet(基于跳表,支持排序与并发修改); - 仅需简单存在性判断且允许一定延迟 → 可用
ConcurrentHashMap模拟(map.put(key, Boolean.TRUE)),空间和性能更优。










