CopyOnWriteArrayList迭代行为不符合预期的技术咨询
解答
嘿,这个问题刚好命中了CopyOnWriteArrayList最核心的设计特性——写时复制(Copy-On-Write),咱们一步步拆解你的理解偏差:
核心机制:迭代器持有创建时的数组快照
CopyOnWriteArrayList的迭代器和普通ArrayList的迭代器完全不同:
- 普通
ArrayList的迭代器是快速失败的,一旦遍历过程中集合被修改,立刻抛出ConcurrentModificationException; - 而
CopyOnWriteArrayList的迭代器,是在你调用iterator()方法的那一刻,对当前集合的底层数组做了一份快照,之后所有的遍历操作都是基于这份快照完成的——哪怕后续集合被修改(add/remove),也不会影响这个迭代器的遍历结果。
结合你的代码具体分析
咱们来梳理你代码里的执行顺序:
- 主线程初始化
CopyOnWriteArrayList,添加了9个元素; - 内部线程启动后,立刻调用了
strings.iterator(),这时候它拿到的是初始9个元素的数组快照; - 主线程随后开始遍历并删除元素:每次调用
strings.remove(value)时,CopyOnWriteArrayList都会复制一份当前的底层数组,在新数组上删除对应元素,然后把内部引用切换到新数组。但这个修改完全不会影响内部线程已经持有的旧数组快照; - 所以内部线程的迭代器会一直遍历自己持有的旧快照,自然能输出所有初始元素,哪怕主线程已经把集合修改为空;
- 最后主线程调用
strings.forEach时,遍历的是最新的空数组,所以不会输出任何内容。
纠正你的预期偏差
你预期内部线程看不到被删除的元素,这是把CopyOnWriteArrayList和普通ArrayList的行为混淆了:
- 普通
ArrayList的迭代器依赖于当前集合的实时状态,修改会触发快速失败; CopyOnWriteArrayList的迭代器是弱一致性的,它不保证能看到集合的最新修改,但能保证遍历过程中不会抛出并发修改异常,适合读多写少的场景(比如配置读取、事件监听列表)。
额外补充:为什么要这么设计?
CopyOnWriteArrayList的设计目标是无锁读:所有读操作(包括遍历)都不需要加锁,性能极高;而写操作因为要复制数组,开销较大,所以只适合写操作很少的场景。它的迭代器快照特性,本质是用空间换时间,牺牲了实时性来换取读操作的高效和安全。
内容的提问来源于stack exchange,提问作者user995656
相关产品推荐
相关产品推荐

