简体   繁体   中英

Java, adding elements in an array an concurrently looping through it

Sorry if this is a dumb question. But could someone explain me what could happens in a scenario like this?

List<Integer> scores = new Arraylist<>() ;

    scores = 
Collections.synchronizedList(scores)

public void add(int element)  {
... 
scores.add(element) 
... 
} 


public String retrieve(int element)  {
... 
For (Integer e : scores).... 
.... 
Return something 
} 

Let's assume that this class is a singelton and that scores is global. Multiple thread can add and retrieve the scores at the same time

In this scenario when starting the for loop and at the same time a thread is adding (or removing an element from the list) will it throw a concurrent modification exeption ?

Thank you

Bad things will happen, given the way you've written your example.

Your retrieve() method doesn't have its loop in a synchronized block, and both of your methods are accessing scores directly, instead of using the List returned by the Collections.synchronizedList() method.

If you take a look at the API for Collections.synchronizedList() , you'll notice that it says

In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.

It is imperative that the user manually synchronize on the returned list when iterating over it:

Failure to follow this advice may result in non-deterministic behavior.

So you might get a ConcurrentModificationException , or something else weird might happen.

Edit

Even if all your access is via the synchronized List , you can still end up getting a ConcurrentModificationException thrown at you if you modify the List while iterating over it in another thread. That's why the Collections.synchronizedList() documentation insists that you manually wrap your iteration inside a block that is synchronized on the List it returns.

The API for ConcurrentModificationException says

For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it . In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected . Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Your add method won't need to be changed, but your retrieve() method should look something like:

public String retrieve(int element) {
    // stuff
    synchronized (scores) { // prevent scores from being modified while iterating
        for (Integer e : scores) {
            // looping stuff
        }
    }
    // more stuff
    return something;
}

Sample Program

Here's a small sample program which demonstrates the behavior of safe vs unsafe access:

public class Scratch {
    private List<Integer> scores = Collections.synchronizedList(new ArrayList<Integer>());

    public static void main(String[] args) throws Exception {
        final Scratch s = new Scratch();
        s.scores.add(1);
        s.scores.add(2);
        s.scores.add(3);

        // keep adding things to the list forever
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int i=100;
                    while (true) {
                        Thread.sleep(100);
                        s.scores.add(i++);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        System.out.println("This will run fine");
        s.safeLoop();

        System.out.println("This will cause a ConcurrentModificationException");
        s.unsafeLoop();
    }

    public void safeLoop() throws InterruptedException {
        synchronized (scores) {
            for (int i : scores) {
                System.out.println("i="+i);
                Thread.sleep(100);
            }
        }
    }

    public void unsafeLoop() throws InterruptedException {
        for (int i : scores) {
            System.out.println("i="+i);
            Thread.sleep(100);
        }
    }
}

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