copyonwritearraylist 的 fail-safe 机制通过迭代器遍历不可变快照实现:写操作加锁复制数组并原子更新引用,读操作无锁且不校验 modcount,故不抛 concurrentmodificationexception;适用于读远多于写的场景。

CopyOnWriteArrayList 的 fail-safe 机制,核心在于“迭代器不操作原列表,而是遍历一个静态快照”——它不靠检测并发修改来规避异常,而是从源头隔离读写操作。
迭代器基于快照,而非实时数组
调用 iterator() 时,CopyOnWriteArrayList 立即把当前底层数组(不可变副本)赋给迭代器。后续所有 next()、hasNext() 都只在这个副本上进行,与原数组是否被其他线程 add/remove 完全无关。
- 即使遍历中途有线程往列表添加了新元素,当前迭代器也看不到,也不会跳过或重复;
- 同样,遍历中某个监听器调用 removeListener(this),也不会影响本次循环的完整性;
- 这个快照一旦生成就固定不变,属于弱一致性语义,不是实时视图。
写操作触发复制,读操作全程无锁
每次 add()、remove() 或 set() 都会:先加 ReentrantLock 锁,再用 Arrays.copyOf() 复制整个数组,在新数组上完成修改,最后用 setArray() 原子更新引用。
- 读操作(get、size、iterator)全部不加锁,可高并发执行;
- 写操作开销与当前数组长度成正比,不适合高频修改;
- 旧数组不会立即回收,由 GC 负责清理,可能带来短时内存压力。
不检查 modCount,彻底绕过 ConcurrentModificationException
普通 ArrayList 迭代器依赖 modCount 和 expectedModCount 对比来判断结构变更;而 CopyOnWriteArrayList 的迭代器压根不维护或校验这两个值。
- 没有 fast-fail 的校验逻辑,自然不会抛 ConcurrentModificationException;
- 这不是“容忍错误”,而是设计上放弃强一致性,换得遍历安全与读性能;
- 它的迭代器甚至不支持 remove() 方法,调用直接抛 UnsupportedOperationException。
典型适用场景明确:读远多于写
fail-safe 不是万能方案,它用空间换时间、用延迟换安全,只在特定模式下优势显著:
- 事件总线中的监听器列表(广播时需遍历,注册/注销频次低);
- 配置项缓存(配置极少变动,但大量线程频繁读取);
- 白名单、权限规则等静态性较强、变更周期长的只读倾向集合。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











