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

时间:2018-05-10 07:42:29

标签: java multithreading concurrency

对于以下代码段:

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

代码始终将输出打印为: 大小:5,要素:11 22 33 44

我无法理解这种行为。当工作线程通过添加一个正确反映了大小的新元素来修改静态列表时,为什么主线程只打印4个元素?鉴于工作线程始终首先执行(睡眠时间较少)。

2 个答案:

答案 0 :(得分:3)

CopyOnWriteArrayList为您提供线程安全的迭代器,因为您可以在同时修改列表时迭代列表,而不会抛出ConcurrentModificationException

逻辑上(实际上并非如此,因为这是低效且不是线程安全的),迭代CopyOnWriteArrayList是这样的:

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

如果在迭代期间对列表进行任何更新,则无关紧要:您正在重复复制&#34;复制&#34;在迭代开始时的列表。

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

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

答案 1 :(得分:2)

这是因为在您的for (Integer it : myList) Iterator创建CopyOnWriteArrayList<Integer>而不是e以及来自Java doc:

  

&#34;快照&#34;样式迭代器方法使用对状态的引用   创建迭代器时的数组。这个数组从不   在迭代器的生命周期中发生了变化,因此干扰就是   不可能,并且保证迭代器不会抛出   ConcurrentModificationException的。迭代器不会反映出来   自迭代器以来添加,删除或更改列表   创建

所以主线程没有看到run()主题的更新,因为在大多数情况下(你在Iterator方法中睡了一觉)e是在Integer主题添加新<a href="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URL}&state=987654321&scope=r_basicprofile">Register With LinkedIn</a> 之前创建的。

更多信息at the documentation