CopyOnWriteArraySet的“无锁去重”实为写时复制+底层加锁+快照语义协同实现:读操作无锁,写操作通过ReentrantLock加锁、数组复制与volatile引用替换保证原子性;去重由addIfAbsent基于equals/hashCode实现,非快照本身职责。

CopyOnWriteArraySet 的“无锁去重”不是靠无锁算法实现的,而是靠写时复制(Copy-On-Write)+ 底层加锁 + 快照语义共同达成的。它读操作无锁,但写操作必须加锁;去重能力来自 Set 接口语义和底层 CopyOnWriteArrayList 的 addIfAbsent 逻辑,而非变量快照本身。
写操作:加锁复制,保证修改原子性
每次 add、remove 或 clear 都会触发以下流程:
- 先获取 ReentrantLock,确保同一时刻只有一个线程执行写操作
- 复制当前底层数组(
Arrays.copyOf),在副本上做修改 - 用 volatile 引用原子替换原数组(
setArray(newElements)) - 旧数组仍被正在迭代的线程持有,新写入对它们不可见
读操作:直接访问,完全无锁
contains、size、isEmpty 等方法全程不加锁:
- 直接读取 volatile 修饰的底层数组引用(
getArray()) - 基于该数组内容计算结果,响应极快
- 即使其他线程正在写,也不会阻塞或报错
迭代器:快照机制,不抛 ConcurrentModificationException
调用 iterator() 时,会立即捕获当前数组的引用并封装成只读迭代器:
- 遍历时始终访问创建那一刻的数组副本,不受后续增删影响
- 这是“弱一致性”的体现:看到的是历史状态,不是实时视图
- 不会因并发修改而抛出
ConcurrentModificationException
去重逻辑:依赖 equals + hashCode,非快照职责
去重不是快照带来的,而是由 addIfAbsent 方法保障:
- 添加前遍历当前数组,检查是否已有
equals()的元素 - 若存在,直接返回 false,不修改数组
- 自定义类必须正确重写
equals()和hashCode(),否则去重失效 - null 元素允许存在(最多一个),判断逻辑单独处理

















