Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone explain me over ConcurrentModificationException?

Tags:

java

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)

like image 236
theJava Avatar asked Feb 16 '23 10:02

theJava


1 Answers

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).
like image 99
Dirk Avatar answered May 14 '23 12:05

Dirk