简体   繁体   English

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

[英]Static collection shared between multiple threads behavior

For the below code snippet: 对于以下代码段:

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

The code always prints output as : size: 5, elements: 11 22 33 44 代码始终将输出打印为:size:5,元素:11 22 33 44

I am not able to understand this behavior. 我无法理解这种行为。 When the worker thread is modifying the static list by adding a new element for which the size is reflected properly then why does the main thread prints only 4 elements? 当工作线程通过添加一个新元素来修改静态列表时,该元素的大小可以正确反映出来,那么为什么主线程只打印4个元素? Given that the worker thread is executed first always(sleep time is less there). 假定总是先执行工作线程(那里的睡眠时间更少)。

A CopyOnWriteArrayList gives you thread-safe iterators, in that you can iterate the list while it is being concurrently modified, without throwing a ConcurrentModificationException . CopyOnWriteArrayList为您提供线程安全的迭代器,因为您可以在同时修改列表的同时对其进行迭代,而不会引发ConcurrentModificationException

Logically (not actually, because this is inefficient and not thread-safe), iterating a CopyOnWriteArrayList is like this: 从逻辑上讲 (不是真正的,因为这效率低下而且不是线程安全的),迭代一个CopyOnWriteArrayList就像这样:

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

If any update to the list occurs during iteration, it doesn't matter: you're iterating a "copy" of the list at the start of the iteration. 如果在迭代过程中对列表进行了任何更新,都没关系:您在迭代开始时迭代列表的“副本”。

You sleep briefly in the thread before adding the element; 在添加元素之前,您先在线程中短暂睡眠; but by that time, the main thread has already started iterating the list. 但是到那时,主线程已经开始迭代该列表。

Since the list initially has 4 elements, and the iteration of the list starts before the thread adds the element, your loop will print out 4 elements. 由于列表最初包含4个元素,并且列表的迭代在线程添加元素之前开始,因此循环将打印出4个元素。

That's because in your for (Integer it : myList) you're creating an Iterator over the CopyOnWriteArrayList<Integer> and from the Java doc: 那是因为在for (Integer it : myList)您正在通过CopyOnWriteArrayList<Integer>和Java文档创建一个Iterator

The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. “快照”样式的迭代器方法在创建迭代器时使用对数组状态的引用。 This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. 此数组在迭代器的生命周期内永不更改,因此不可能发生干扰,并且保证迭代器不会引发ConcurrentModificationException。 The iterator will not reflect additions, removals, or changes to the list since the iterator was created. 自创建迭代器以来,该迭代器将不会反映对列表的添加,删除或更改。

So the main thread won't see the update from the e thread because in most cases ( you've a sleep in the run() method) the Iterator is created before the e thread adds a new Integer . 因此,主线程将看不到e线程的更新,因为在大多数情况下 (您在run()方法中处于睡眠状态), Iterator是在e线程添加新Integer之前创建的。

Further info at the documentation . 有关更多信息,请参见文档

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

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