4

Case 1: This does not cause ConcurrentModificationException?. Can anyone tell me why does this not cause ConcurrentModificationException.

public class UpdatePeople {
    List < People > records = new ArrayList < People > ();

    public class AsyncTask extends AsyncTask < Void, Void, Boolean > {
        List < People > people;

        public AsyncTask(List < People > allergy) {
            this.people = people;
        }@
        Override
        protected Boolean doInBackground(Void...params) {
            List < String > responses = new ArrayList < String > ();
            for (People peopleList: this.people) {

            }

        }

    }
}

Case 2: This causes ConcurrentModificationException as i am trying to access the List of people in my AsyncThread which is not thread safe. I can make my List of People implement CopyOnWriteArrayList which is thread safe and this should work.

public class UpdatePeople {
        List < People > records = new ArrayList < People > ();

        public class AsyncTask extends AsyncTask < Void, Void, Boolean > {
            @
            Override
            protected Boolean doInBackground(Void...params) {
                List < String > responses = new ArrayList < String > ();
                for (People peopleList: records) {

                }

            }

        }
    }
  1. Can anyone explain me what is exactly happening in case 1. I am not able to understand how this solves the ConcurrentModificationException issue.
  2. Is the case 2 changing the implementation from ArrayList to CopyOnWriteArrayList recommended?

Adding the exception:

05-28 20:34:21.073: E/XXX(904): Uncaught exception is: 05-28 20:34:21.073: E/XXX(904): java.lang.RuntimeException: An error occured while executing doInBackground() 05-28 20:34:21.073: E/XXX(904): at android.os.AsyncTask$3.done(AsyncTask.java:299) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.FutureTask.run(FutureTask.java:137) 05-28 20:34:21.073: E/XXX(904): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 05-28 20:34:21.073: E/XXX(904): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 05-28 20:34:21.073: E/XXX(904): at java.lang.Thread.run(Thread.java:856) 05-28 20:34:21.073: E/XXX(904): Caused by: java.util.ConcurrentModificationException 05-28 20:34:21.073: E/XXX(904): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)

theJava
  • 14,620
  • 45
  • 131
  • 172
  • please add the message of the exception and the code that is inside the "for (People peopleList: records)" loop to your question – mschenk74 May 28 '13 at 14:56
  • @mschenk74: The exeception in the second case which i am getting is on the loop for (People peopleList: records) – theJava May 28 '13 at 14:58
  • You got your hand slapped for modifying an object while there was an iterator over it. – Hot Licks May 28 '13 at 14:58
  • @HotLicks but the question is why it doesn't occur in the first case – mschenk74 May 28 '13 at 15:00
  • In case 1 you iterate over `people` which is local to your AsyncTask (at least the reference is). And in case 2 you iterate over `records` which will be shared by as many AsyncTasks as you have simultaneously running. Might that be the reason? – Simon May 28 '13 at 15:01
  • Could you again describe how you modify the list? – mschenk74 May 28 '13 at 15:07
  • Should, in the first one, it be: this.people = allergy; ? If it's really this.people = people, the first one is iterating over an empty list. – user1676075 May 28 '13 at 15:16

1 Answers1

8

Generally speaking, a ConcurrentModificationException is raised, if you try to modify a collection while iterating over it. For example:

public class Main {

    public static void main(String[] args) 
    throws Exception {

        final List<Object> c1 = new ArrayList<Object>(Arrays.<Object>asList(1, 2, 3));
        for (Object o: c1) c1.remove(o);
    }
}

will cause a java.util.ConcurrentModificationException at run-time, since we are modifying the list while iterating over it (NB: only a single thread involved here). This is detected by the list's iterator and causes the exception.

If the desired modification is the deletion of the very element just received from the iterator, you can achieve the desired result (in the single threaded case!) by working with the iterator directly. Replace the for(each) loop with:

final Iterator<Object> iterator = c1.iterator();

while (iterator.hasNext()) {

    final Object o = iterator.next();
    if (satisfiesSomeCriterion(o)) iterator.remove();
}

This does, however, not work for all modifications. Additions, for example, won't work. The following code does still fail:

for (Object o: c1) c1.add(Integer.valueOf(((Integer)o).intValue() + 10));

If the underlying collection is a List, you may be able to work around this restriction by using a ListIterator instead of a plain iterator:

final ListIterator<Integer> iterator = c1.listIterator();

while (iterator.hasNext()) {

    final Integer o = iterator.next();
    if (satisfiesSomeCriterion(o)) iterator.add(Integer.valueOf(o.intValue() + 10);
}

but note, that this is not equivalent to the code given above (other placement of the element being inserted). Also note, that ListIterator.add is an optional method; an implementation might throw an UnsupportedOperationException when it is being called.

All the things said above are applicable for the single-threaded case only. A whole new set of problems arises, if you try to access the same collection from multiple threads concurrently without proper synchronization, as this has not only the potential to cause ConcurrentModificationExceptions in the iterating thread, but also to destroy the integrity of the collection's internal data structures.

There are a few things you can do:

  • Use a concurrency-aware collections (such as the CopyOnWriteArrayList you already mentioned)
  • Wrap the collection via Collections.synchronizedList (...Set, ...Whatever). Note, though, that in this case you are still responsible for providing a proper locking discipline with respect to iteration. See this answer for details.
  • Make a copy of the original collection before passing it to the background job (and make sure, that the background job is the only thread using that copy)
  • Protect all uses of the collection by synchronized blocks (for example, using the collection itself as the "monitor" object, though better: using a dedicated monitor object).
Community
  • 1
  • 1
Dirk
  • 30,623
  • 8
  • 82
  • 102