copyonwritearrayset基于copyonwritearraylist实现线程安全set,读无锁、写复制,适合读多写少场景;不支持null,非原子性操作需额外同步。

CopyOnWriteArraySet 通过底层使用 CopyOnWriteArrayList 实现线程安全的 Set 操作,核心思路是“读不加锁、写时复制”,牺牲写性能换取读的无锁并发性。
底层基于 CopyOnWriteArrayList 构建
CopyOnWriteArraySet 内部持有一个 CopyOnWriteArrayList 实例,所有操作(add、remove、contains)都委托给它。它本身不存储元素,也不维护哈希表或红黑树,而是依赖 ArrayList 的线程安全特性 + 手动去重逻辑来模拟 Set 行为。
每次添加元素前,它会先遍历当前数组判断是否已存在——若不存在才调用底层 list 的 add 方法;这个查找过程是同步在当前快照上的,不会受其他线程写操作影响。
写操作触发数组复制,读操作始终无锁
当执行 add 或 remove 时,CopyOnWriteArraySet 会:
- 获取当前内部 list 的数组快照(不可变引用)
- 在新数组上完成修改(如新增元素、过滤旧元素)
- 用 CAS 或 synchronized 替换原数组引用
由于读操作(比如 contains、iterator)只访问当前数组引用指向的数组,且该数组在写入期间不会被修改,因此无需加锁,也不会抛出 ConcurrentModificationException。
适合读多写少、迭代频繁的场景
它的优势在高并发读、极少修改的场景下非常明显。例如:
- 监听器集合(Listener List):大量线程反复遍历检查回调,但注册/注销动作很少
- 配置白名单、状态标记集合:只在初始化或运维时变更,运行期只读
但要注意:写操作开销大(每次都要复制整个数组),且迭代器看到的是创建时刻的快照,无法反映后续写入——这不是 bug,而是设计取舍。
不支持 null 元素,也不保证实时一致性
和 CopyOnWriteArrayList 一样,CopyOnWriteArraySet 明确禁止 null 元素,add(null) 会直接抛出 NullPointerException。
另外,它的 size()、isEmpty() 等方法返回的是快照值;多个操作之间没有原子性。例如:
if (!set.contains(x)) set.add(x); // 非原子,仍可能重复添加这种“检查-添加”逻辑不能靠它保证线程安全,需要额外同步或改用 ConcurrentHashMap + computeIfAbsent 等方式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











