繁体   English   中英

多线程之间共享静态集合的行为

[英]Static collection shared between multiple threads behavior

对于以下代码段:

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:");
    }
}

代码始终将输出打印为:size:5,元素:11 22 33 44

我无法理解这种行为。 当工作线程通过添加一个新元素来修改静态列表时,该元素的大小可以正确反映出来,那么为什么主线程只打印4个元素? 假定总是先执行工作线程(那里的睡眠时间更少)。

CopyOnWriteArrayList为您提供线程安全的迭代器,因为您可以在同时修改列表的同时对其进行迭代,而不会引发ConcurrentModificationException

从逻辑上讲 (不是真正的,因为这效率低下而且不是线程安全的),迭代一个CopyOnWriteArrayList就像这样:

List<Integer> copyOfList = new ArrayList<>(myList);
for (Integer it : copyOfList) {
  // ...
}

如果在迭代过程中对列表进行了任何更新,都没关系:您在迭代开始时迭代列表的“副本”。

在添加元素之前,您先在线程中短暂睡眠; 但是到那时,主线程已经开始迭代该列表。

由于列表最初包含4个元素,并且列表的迭代在线程添加元素之前开始,因此循环将打印出4个元素。

那是因为在for (Integer it : myList)您正在通过CopyOnWriteArrayList<Integer>和Java文档创建一个Iterator

“快照”样式的迭代器方法在创建迭代器时使用对数组状态的引用。 此数组在迭代器的生命周期内永不更改,因此不可能发生干扰,并且保证迭代器不会引发ConcurrentModificationException。 自创建迭代器以来,该迭代器将不会反映对列表的添加,删除或更改。

因此,主线程将看不到e线程的更新,因为在大多数情况下 (您在run()方法中处于睡眠状态), Iterator是在e线程添加新Integer之前创建的。

有关更多信息,请参见文档

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM