CopyOnWriteArrayList 的线程安全需满足四大约束:配置对象必须不可变;读操作即用即取、不缓存快照;写操作频次低且规模小(≤500项);接受最终一致性。它适用于开关类场景,不适用于强一致需求。

CopyOnWriteArrayList 的“写时复制”不是自动解决线程安全的银弹,它只在满足特定约束时才真正安全。核心不在容器本身,而在你如何用它——尤其是配置对象的设计和读写方式是否匹配其快照语义。
配置对象必须不可变
写操作复制的是数组引用,但数组里存的是对象引用。如果 FeatureFlag 这类配置对象内部字段可变(比如有 setEnabled() 方法),多个线程可能同时读旧副本、写新副本,导致状态混乱。
- 所有字段声明为 final,构造时一次性赋值
- 不提供任何 setter 或可变方法
- 每次更新都新建实例:new FeatureFlag("auth", false, "2026-Q2"),而不是复用旧对象再改属性
读操作要“即用即取”,避免持有过期快照
迭代器返回的是创建那一刻的数组副本,后续写操作不会反映其中。若缓存了 List 引用或长期复用同一个 Iterator,就可能持续读到几分钟前的配置。
- 每次读取都调用 list.get(i) 或 list.toArray(),不缓存 list 变量本身
- 禁止用 for-each 循环遍历后长期持有 iterator 对象
- 如需按 key 查找,别用 stream.filter —— 全量扫描旧副本效率低,应搭配 ConcurrentHashMap 做索引,写时同步更新两者
写操作频次和规模要严控
每次 add/set/remove 都触发 O(n) 数组复制。500 条以内配置项,单次写耗时通常在 0.1–0.5ms;超千条时易出现毫秒级卡顿和老年代 GC 压力。
- 写操作间隔建议 ≥ 数秒,避免连续触发(例如后台批量推送要合并成一次)
- 配置总数控制在 500 条内,若规则增长快,考虑分片(如按业务域拆成多个 CopyOnWriteArrayList)
- 禁用直接操作底层数组(如 getArray() 后手动修改),必须走容器提供的 add/set/remove 方法
接受“最终一致性”,不强求实时可见
它不保证写完立刻被所有读线程看到,只保证“读操作绝不会失败、不会阻塞、不会看到中间态”。这是设计取舍,不是缺陷。
- 适合开关、路由、限流阈值等场景——延迟几百毫秒生效完全可接受
- 不适合需要强一致的计数器、余额、会话状态等场景
- 若业务真需要“写后立即可见”,说明它不匹配,该换用 ReadWriteLock + ArrayList 或其它方案

















