copyonwritearrayset适合读多写少的监听器列表场景,因其写时复制机制保障遍历安全、无锁并发读取,且注册/注销线程安全;但写操作有复制开销,内存占用高,数据最终一致。

CopyOnWriteArraySet 适合用在读多写少、且写操作不频繁的监听器列表场景中,它通过写时复制(Copy-On-Write)机制实现线程安全,无需显式加锁,天然支持高并发读取和安全的动态增删。
为什么监听器列表适合用 CopyOnWriteArraySet
事件监听器通常被大量线程频繁遍历(如事件分发),但注册/注销操作相对较少。CopyOnWriteArraySet 底层基于 CopyOnWriteArrayList 实现去重,每次修改(add/remove)都会创建新数组副本,读操作永远访问不可变快照,因此:
- 遍历时不会抛
ConcurrentModificationException - 多个线程同时遍历互不影响,无同步开销
- 写操作虽有复制开销,但在监听器增删不频繁时可接受
基本用法:声明与注册/注销监听器
直接声明为 CopyOnWriteArraySet 类型,无需额外同步:
private final CopyOnWriteArraySet<eventlistener> listeners = new CopyOnWriteArraySet();
// 注册监听器(线程安全)
public void addListener(EventListener listener) {
listeners.add(listener);
}
// 注销监听器(线程安全)
public void removeListener(EventListener listener) {
listeners.remove(listener);
}</eventlistener>
事件分发时安全遍历
遍历过程完全无锁,即使其他线程正在增删,当前遍历仍基于旧快照执行:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
public void fireEvent(Event event) {
// 遍历的是当前快照,安全可靠
for (EventListener listener : listeners) {
try {
listener.onEvent(event);
} catch (Exception e) {
// 建议捕获单个监听器异常,避免影响其他监听器
log.warn("Listener execution failed", e);
}
}
}
注意:不能在遍历中调用 remove() 或 add() —— 这不是 CopyOnWriteArraySet 的限制,而是增强 for 循环本质是迭代器遍历,而它的迭代器不支持修改(UnsupportedOperationException)。但你本就不该在遍历时改集合,应统一由外部控制增删时机。
注意事项与替代考虑
虽然方便,但需留意其适用边界:
- 内存占用略高:每次写操作都复制整个数组,大数据量监听器列表会增加 GC 压力
- 数据一致性是“最终一致”:新注册的监听器不会收到正在分发中的事件,只对后续事件生效
- 不适用于写频次高的场景(如每毫秒注册/注销)
- 如果需要有序监听器或更细粒度控制(如按优先级排序),可考虑封装 + 手动同步,或选用
ConcurrentHashMap+ConcurrentLinkedQueue组合
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










