以下是 CopyOnWriteArrayList
和 CopyOnWriteArraySet
的核心特性、使用场景、优缺点及对比总结:
1. CopyOnWriteArrayList
核心特性
- 线程安全:通过写时复制(Copy-On-Write)机制实现线程安全。
- 读操作无锁:读取时无需锁,性能高,适合读多写少的场景。
- 写操作耗时:添加、删除或修改元素时,会复制整个底层数组,内存开销较大。
- 迭代器弱一致性:遍历时看到的是快照数据,不会抛出
ConcurrentModificationException
。
典型方法
方法 | 说明 |
---|
add(E e) | 添加元素,触发数组复制。 |
remove(Object o) | 删除元素,触发数组复制。 |
set(int index, E element) | 修改指定位置的元素,触发数组复制。 |
iterator() | 返回迭代器,遍历时基于快照(复制时的数组状态)。 |
使用场景
- 读多写少:如配置列表、观察者模式中的注册表。
- 需要避免阻塞读操作:如日志记录、缓存列表。
优缺点
优点 | 缺点 |
---|
读操作高性能,无锁机制。 | 写操作性能低(需复制数组)。 |
支持安全的迭代,无需额外同步。 | 内存消耗高(写操作时复制整个数组)。 |
线程安全,无需手动加锁。 | 不适合频繁写入的场景。 |
2. CopyOnWriteArraySet
核心特性
- 基于
CopyOnWriteArrayList
实现:底层通过 CopyOnWriteArrayList
存储元素,保证唯一性。 - 线程安全:继承自
CopyOnWriteArrayList
的线程安全机制。 - Set 特性:不允许重复元素,元素无序。
典型方法
方法 | 说明 |
---|
add(E e) | 添加元素,若已存在则不操作。 |
remove(Object o) | 删除元素。 |
contains(Object o) | 检查元素是否存在。 |
iterator() | 返回迭代器,遍历基于快照。 |
使用场景
- 线程安全的唯一元素集合:如线程安全的注册表、唯一标识符集合。
- 需要避免重复元素的场景:如用户黑名单、唯一资源池。
优缺点
优点 | 缺点 |
---|
线程安全,无需手动加锁。 | 写操作性能低(需复制底层数组)。 |
元素唯一,自动去重。 | 内存消耗高(写操作时复制整个数组)。 |
支持安全的迭代。 | 查找性能不如 HashSet (需遍历数组)。 |
3. 对比表格
特性 | CopyOnWriteArrayList | CopyOnWriteArraySet |
---|
数据结构 | 底层是数组,有序,允许重复元素。 | 底层基于 CopyOnWriteArrayList ,元素唯一。 |
线程安全机制 | 写时复制(Copy-On-Write)。 | 写时复制,继承自 CopyOnWriteArrayList 。 |
读操作性能 | 高(无锁)。 | 高(无锁)。 |
写操作性能 | 低(需复制数组)。 | 低(需复制数组)。 |
元素唯一性 | 允许重复元素。 | 确保元素唯一。 |
适用场景 | 读多写少的列表场景。 | 需要线程安全且元素唯一的集合场景。 |
迭代器特性 | 基于快照,弱一致性。 | 基于快照,弱一致性。 |
4. 使用示例
CopyOnWriteArrayList
示例
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
list.forEach(System.out::println);
list.set(0, "C");
CopyOnWriteArraySet
示例
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("A");
set.add("A");
System.out.println(set.contains("A"));
set.remove("A");
5. 选择建议
- 选
CopyOnWriteArrayList
的场景:
- 需要有序列表或允许重复元素。
- 需要频繁读取,偶尔写入。
- 选
CopyOnWriteArraySet
的场景:
- 需要线程安全的唯一元素集合。
- 元素无需有序,但需保证唯一性。
注意事项
- 避免频繁写操作:两个类的写操作都会复制数组,内存和性能开销较大。
- 内存占用:写操作时内存消耗是原数组的两倍(旧数组保留直到垃圾回收)。
- 替代方案:
- 若写操作频繁,可考虑
ConcurrentHashMap
的键集合(keySet()
)。 - 若需要高性能的唯一集合,可使用
ConcurrentSkipListSet
(基于跳表)。
通过合理选择,可在保证线程安全的同时满足不同场景的需求。