简体   繁体   中英

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

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? 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 .

Logically (not actually, because this is inefficient and not thread-safe), iterating a CopyOnWriteArrayList is like this:

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.

That's because in your for (Integer it : myList) you're creating an Iterator over the CopyOnWriteArrayList<Integer> and from the Java doc:

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. 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 .

Further info at the documentation .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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