java.util.concurrentmodificationexception – How to handle Concurrent Modification Exception
The java.util.concurrentmodificationexception is a RuntimeException
that may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. An example of not permissible behavior is when a thread tries to modify the internal structure of a Collection
, while another thread is iterating over it.
In general, the results of the iteration are undefined. Some iterators throw a java.util.concurrentmodificationexception
when they detect such behavior. These iterators are called fail-fast iterators
, as they stop the normal execution to report an error, rather than continuing in a non-deterministic way. Notice that this exception does not indicate that the object has been concurrently modified by a different thread. The exception is thrown even one thread is violating an object’s contract.
1. Error cases
In this section, we will describe and explain those cases that produce a java.util.concurrentmodificationexception.
Case 1: The Collection is internally modified, while a thread is iterating over it.
Example_v1.java:
import java.util.HashMap; import java.util.Map; public class Example_v1 { public static void main(String[] args) { Map<String, Integer> map = new HashMap<String, Integer>(); // Insert some sample key-value pairs. map.put("Key1", 1); map.put("Key2", 2); map.put("Key3", 3); /* Remove a value of the map, while iterating over it. * The following code throws a java.util.ConcurrentModificationException. */ for(String key: map.keySet()) { if(map.get(key) == 1) map.remove(key); } System.out.println("Successfully removed a pair!"); } }
The result of the execution is:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926) at java.util.HashMap$KeyIterator.next(HashMap.java:960) at main.java.Example.main(Example.java:18)
The exception is thrown because we change the internal structure of the HashMap
by removing an existing key, while we iterating over it.
Case 2: After the creation of an iterator, the Collection is internally modified by any method other than the iterator’s own methods for removal and addition.
Example_v2.java:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Example_v2 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); // Insert some sample values. list.add("Value1"); list.add("Value2"); list.add("Value3"); // Get an iterator. Iterator<String> ite = list.iterator(); /* Remove the first object of the list. This statement will force the iterator * to throw a ConcurrentModificationException. */ list.remove(0); while(ite.hasNext()) System.out.println(ite.next()); } }
The result of the execution is:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at main.java.Example.main(Example.java:25)
The exception is thrown because:
- We create an iterator of the
ArrayList
. - We remove an object using the
remove
method, rather than the iterator’s ownremove
method. - We try to iterate over the list and thus, a java.util.concurrentmodificationexception is thrown.
Case 3: Two iterators simultaneously modify the internal structure of a Collection.
Example_v3.java:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Example_v3 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); // Insert some sample values. list.add("Value1"); list.add("Value2"); list.add("Value3"); // Get two iterators. Iterator<String> ite = list.iterator(); Iterator<String> ite2 = list.iterator(); // Point to the first object of the list and then, remove it. ite.next(); ite.remove(); /* The second iterator tries to remove the first object as well. The object does * not exist and thus, a ConcurrentModificationException is thrown. */ ite2.next(); ite2.remove(); } }
The result of the execution is:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at main.java.Example.main(Example.java:28)
The exception is thrown because:
- We create two iterators of the
ArrayList
. - The 1st iterator modifies the internal structure of the list, by removing its first object.
- The 2nd iterator tries to remove the first object as well, but the first object does not exist and thus, a
ConcurrentModificationException
is thrown.
2. Deal with the java.util.concurrentmodificationexception Exception
First of all, we must understand how Java decides that a collection is modified concurrently and a ConcurrentModificationException
must be thrown. In Java 7, the implementation of the ArrayList
uses the following field, to provide a fail-fast iterator:
protected transient int modCount;
Following the same logic, the implementation of the HashMap
uses the following field:
transient int modCount;
In both implementations, the modCount
field indicates the number of times the collection has been structurally modified. For example, a structural modification can be an operation that changes the number of mappings in a HashMap
, or an operation that changes the size of an ArrayList
.
In any case, if the value of the modCount
field changes unexpectedly, a ConcurrentModificationException
is thrown.
In the rest of this section, we will discuss techniques and tricks that help us avoid the ConcurrentModificationException
:
Iterator’s remove method
In a single-threaded environment, use the iterator’s remove
method, in order to concurrently iterate over a collection and remove things from it. For example:
Example_v4.java:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Example_v4 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); // Insert some sample values. list.add("Value1"); list.add("Value2"); list.add("Value3"); // Get an iterator. Iterator<String> ite = list.iterator(); /* Remove the second value of the list, while iterating over its elements, * using the iterator's remove method. */ while(ite.hasNext()) { String value = ite.next(); if(value.equals("Value2")) ite.remove(); else System.out.println(value); } } }
Synchronization
In order to avoid more than one thread accessing or modifying the same object, you can synchronize them over the object, in order to allow only one thread to manipulate it over time. However, notice that this approach may reduce the performance of your application, or create deadlocks if the application has not been developed carefully.
Synchronized Collections
In addition to their default implementations, Java provides a synchronized implementation of a Map
, a List
, a Set
, a Collection
, etc. through the Collections
class. Moreover, Java provides the CopyOnWriteArrayList
class, in which all mutative operations are implemented by making a fresh copy of the underlying array. Finally, Java also provides the ConcurrentHashMap
class, which offers full concurrency of retrievals and adjustable expected concurrency for updates.
All referenced implementations are thread-safe. However, the usage of such data structures may also reduce the performance of your application, as thread synchronization spends CPU cycles.
To conclude, all aforementioned methods aim to eliminate the ConcurrentModificationException
. However, in a multi-threaded environment, this elimination usually comes with the cost of thread synchronization. In any case, each application has its own specifications and requirements and thus, a meticulous design and implementation are very important in order for such exceptions to be eliminated.
3. Download The Eclipse Project
This was a tutorial on how to handle the ConcurrentModificationException
.
You can download the full source code of this example here: java.util.concurrentmodificationexception – How to handle Concurrent Modification Exception
Last updated on Dec. 09th, 2021
Thanks man, you saved me!