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

多线程共享CopyOnWriteArrayList:添加元素后遍历与size不符疑问

为什么CopyOnWriteArrayList添加元素后,主线程遍历还是看不到新元素?

先看你提供的代码:

public class ThreadExample extends Thread { 
    static List<Integer> myList = new CopyOnWriteArrayList<Integer>(); 
    public static void main(String[] args) throws InterruptedException { 
        myList.add(11); 
        myList.add(22); 
        myList.add(33); 
        myList.add(44); 
        ThreadExample e = new ThreadExample(); 
        e.start(); 
        for (Integer it : myList) { 
            try { 
                Thread.sleep(1000); 
            } catch (Exception e1) { 
                System.out.print("e1 "); 
            } 
            System.out.print(" " + it); 
        } 
    } 
    public void run() { 
        try { 
            Thread.sleep(50); 
        } catch (Exception e) { 
            System.out.print("e2 "); 
        } 
        myList.add(77); 
        System.out.print("size: " + myList.size() + ", elements:"); 
    } 
}

你的困惑完全合理:工作线程明明先完成了add(77)操作,列表的size()也正确返回了5,但主线程遍历却只输出4个元素。这背后的核心原因是CopyOnWriteArrayList的「写时复制+迭代器快照」特性。

具体拆解事件顺序:

  1. 主线程先往列表里加入4个元素,启动工作线程后,立刻进入for-each循环。
  2. 当for-each循环启动时,会隐式创建一个CopyOnWriteArrayList的迭代器。这个迭代器是基于创建它那一刻的列表底层数组快照构建的——此时工作线程还在50ms的sleep中,列表只有4个元素,所以迭代器拿到的就是这4个元素的快照。
  3. 工作线程sleep结束后执行myList.add(77):CopyOnWriteArrayList的写操作会复制一份当前的底层数组,在新数组上添加元素77,再把列表的底层引用切换到新数组。这时候调用size()会返回新数组的长度5,所以你能看到size: 5的输出。
  4. 但主线程的迭代器已经绑定了旧的、只有4个元素的数组快照,它不会感知到后续底层数组的替换。所以哪怕列表现在实际有5个元素,迭代器还是会老老实实遍历旧快照里的4个元素。

验证这个逻辑

如果我们修改主线程,让它在进入循环前等待工作线程完成添加操作,比如加一行Thread.sleep(100):

e.start();
Thread.sleep(100); // 等待工作线程完成元素添加
for (Integer it : myList) {
    // ... 原代码
}

这时候迭代器会在工作线程添加元素后创建,拿到的是包含5个元素的快照,遍历就会输出11 22 33 44 77了。

简单来说,CopyOnWriteArrayList的迭代器是「只读快照」,它和后续的写操作完全隔离——这也是它在并发场景下无需加锁就能安全遍历的原因,但代价就是迭代器无法实时反映列表的最新状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:52:29