You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CopyOnWriteArrayList迭代行为不符合预期的技术咨询

解答

嘿,这个问题刚好命中了CopyOnWriteArrayList最核心的设计特性——写时复制(Copy-On-Write),咱们一步步拆解你的理解偏差:

核心机制:迭代器持有创建时的数组快照

CopyOnWriteArrayList的迭代器和普通ArrayList的迭代器完全不同:

  • 普通ArrayList的迭代器是快速失败的,一旦遍历过程中集合被修改,立刻抛出ConcurrentModificationException;
  • 而CopyOnWriteArrayList的迭代器,是在你调用iterator()方法的那一刻,对当前集合的底层数组做了一份快照,之后所有的遍历操作都是基于这份快照完成的——哪怕后续集合被修改(add/remove),也不会影响这个迭代器的遍历结果。

结合你的代码具体分析

咱们来梳理你代码里的执行顺序:

  1. 主线程初始化CopyOnWriteArrayList,添加了9个元素;
  2. 内部线程启动后,立刻调用了strings.iterator(),这时候它拿到的是初始9个元素的数组快照;
  3. 主线程随后开始遍历并删除元素:每次调用strings.remove(value)时,CopyOnWriteArrayList都会复制一份当前的底层数组,在新数组上删除对应元素,然后把内部引用切换到新数组。但这个修改完全不会影响内部线程已经持有的旧数组快照;
  4. 所以内部线程的迭代器会一直遍历自己持有的旧快照,自然能输出所有初始元素,哪怕主线程已经把集合修改为空;
  5. 最后主线程调用strings.forEach时,遍历的是最新的空数组,所以不会输出任何内容。

纠正你的预期偏差

你预期内部线程看不到被删除的元素,这是把CopyOnWriteArrayList和普通ArrayList的行为混淆了:

  • 普通ArrayList的迭代器依赖于当前集合的实时状态,修改会触发快速失败;
  • CopyOnWriteArrayList的迭代器是弱一致性的,它不保证能看到集合的最新修改,但能保证遍历过程中不会抛出并发修改异常,适合读多写少的场景(比如配置读取、事件监听列表)。

额外补充:为什么要这么设计?

CopyOnWriteArrayList的设计目标是无锁读:所有读操作(包括遍历)都不需要加锁,性能极高;而写操作因为要复制数组,开销较大,所以只适合写操作很少的场景。它的迭代器快照特性,本质是用空间换时间,牺牲了实时性来换取读操作的高效和安全。

内容的提问来源于stack exchange,提问作者user995656

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:11:24