Reputation: 15034
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) {
}
}
}
}
case 1
. I am not able to understand how this solves the ConcurrentModificationException
issue.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)
Upvotes: 4
Views: 2855
Reputation: 31053
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 ConcurrentModificationException
s 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:
CopyOnWriteArrayList
you already mentioned)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.synchronized
blocks (for example, using the collection itself as the "monitor" object, though better: using a dedicated monitor object).Upvotes: 8