2

I have an ArrayList of Strings and two threads are concurrently accessing the list. What will the output of following snippet and why?

public static void main(String[] args) {
    final ArrayList<String> list = new ArrayList<String>();

    for (int i = 0; i < 100; i++) {
        list.add("Number" + i);
    }

    new Thread() {

        public void run() {
            for (String s : list) {
                System.out.println(s);
            }
        }
    }.start();

    new Thread() {
        public void run() {
            list.remove("Number5");
        }
    }.start();

}

I tried using the same code making the Arraylist synchronized using Collections.synchronizedList(list). It is still throwing java.util.ConcurrentModificationException.

dsharew
  • 10,377
  • 6
  • 49
  • 75
Dhananjay
  • 69
  • 1
  • 10

4 Answers4

3

The Javadoc of synchronizedList clearly states that:

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

List list = Collections.synchronizedList(new ArrayList());
 ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

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

Since in this code you are removing from a List while you are iterating over it (in a different thread but it is the same instance of the List), a ConcurrentModificationException will be thrown.

Using Collections.synchronizedList, the following code will run fine.

final List<String> list = Collections.synchronizedList(new ArrayList<String>());
for (int i = 0; i < 100; i++) {
    list.add("Number" + i);
}

new Thread() {
    public void run() {
        synchronized (list) {
            for (String s : list) {
                System.out.println(s);
            }
        }
    }
}.start();

new Thread() {
    public void run() {
        synchronized (list) {
            list.remove("Number5");
        }
    }
}.start();
Tunaki
  • 132,869
  • 46
  • 340
  • 423
2

You can put a lock on the list object using syncrhonized keyword.

Synchronized Keyword
Its overall purpose is to only allow one thread at a time into a particular section of code thus allowing us to protect, for example, variables or data from being corrupted by simultaneous modifications from different threads.

So this should work for you:

public static void main(String[] args) {
    ...

    new Thread() {

        public void run() {
        synchronized(list){
            for (String s : list) {
                System.out.println(s);
            }
        }
        }
    }.start();

    new Thread() {
        public void run() {
        synchronized(list){
                    list.remove("Number5");
        }
        }
    }.start();

}

NB: It depends on what logic you want btw. Do you want remove from the list as you iterate over it or? you want the two tasks to happen sequentially?

Community
  • 1
  • 1
dsharew
  • 10,377
  • 6
  • 49
  • 75
  • 1
    me too: was looking for the purpose. If required, I guess we will have to interleave the tasks between the two threads. Moreover, adding and removing is similar to a producer-consumer problem, (Printing the whole list requires the lock on whole list object, depending on the intention of the developer.) – a3.14_Infinity Oct 16 '15 at 09:55
  • yeah exactly it all depends on the intention of the developer. But the developer is not commenting so.. :-) – dsharew Oct 16 '15 at 09:57
1

You are iterating (in first thread) and updating (in second thread) the same list object in the two different threads, so there is always (possibility) that it will throw java.util.ConcurrentModificationException.

In the java Iterator are fail-fast in nature, so as soon as they realize that underline structure has been changed, they will fail with the ConcurrentModificationException exception.

If you want to use the same list object for you needs, you consider synchronizing it, using the synchronized(list) in both the threads run method.

    public void run() {
        synchronized(list) {
          for (String s : list) {
              System.out.println(s);
          }
        }
    }

    public void run() {
        synchronized(list) {
          list.remove("Number5");
        }
    }
YoungHobbit
  • 13,254
  • 9
  • 50
  • 73
0

I think you should use Vector instead of ArrayList for being ThreadSafe plus using Iterator for browsing your List.

Irshad
  • 3,071
  • 5
  • 30
  • 51