Table of Contents
Introduction
If you run the following code:
public static void main(String[] args) { // Create a new ArrayList ArrayList<String> list = new ArrayList<>(); // Add some elements to the list list.add("item1"); list.add("item2"); list.add("item3"); // Modify the list concurrently in multiple threads new Thread(() -> { try { Thread.sleep(1000); list.add("item4"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); // Iterate over the list try { Thread.sleep(1000); for (String item : list) { System.out.println(item); } } catch (InterruptedException e) { e.printStackTrace(); } }
There are chances you will get the following exception:
Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967) at com.datmt.java_core.concurrency.ConcurrentModificationProblem.main(ConcurrentModificationProblem.java:31)
I said “there are chances” because you run the same code repeatedly, and sometimes there is no exception thrown.
So, what is ConcurrentModificationException
and when you might encounter it.
The JDK’s said:
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
One example is one thread modifies an array list and another thread iterates over it.
This is exactly what happened above. The first thread adds “item4” to the list while the main thread iterates over the list.
If you change the value in the sleep methods of each thread to completely different numbers (1000, 2000 for example), the exception is not likely to be thrown.
This exception can be thrown in one thread. This is the case when you iterate over a collection using an iterator and try to modify it.
Here is the example:
private static void sample1() { ArrayList<String> list = new ArrayList<>(); // Add some elements to the list list.add("item1"); list.add("item2"); list.add("item3"); // Create an iterator for the list Iterator<String> iterator = list.iterator(); // Iterate over the list using the iterator while (iterator.hasNext()) { String item = iterator.next(); list.add("meme"); System.out.println(item); } }
This method is different from the first example because the exception is always thrown.
How to avoid ConcurrentModificationException
Now you know the cause of ConcurrentModificationException. Let’s see what are the options to avoid it.
The most obvious way (but not always practical) is
- not to modify the collection while iterating over it
- Not to modify / access the collection from different threads.
However, these options are not always possible. There are cases when you need to make a collection to be available to multiple threads.
A decent option is to use thread-safe collections.
Thread-safe collections
In the first example, replacing ArrayList
with its thread-safe counterpart: CopyOnWriteArrayList
would fix the problem:
public static void main(String[] args) { // Create a new ArrayList CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); // Add some elements to the list list.add("item1"); list.add("item2"); list.add("item3"); // Modify the list concurrently in multiple threads new Thread(() -> { list.add("item4"); }).start(); // Iterate over the list for (String item : list) { System.out.println(item); } }
Here is the thread-safe and non-thread-safe collections in one table:
Thread-safe Collection | Non-thread-safe Counterpart |
---|---|
java.util.concurrent.CopyOnWriteArrayList | java.util.ArrayList |
java.util.concurrent.CopyOnWriteArraySet | java.util.HashSet |
java.util.concurrent.ConcurrentHashMap | java.util.HashMap |
java.util.concurrent.ConcurrentSkipListMap | java.util.TreeMap |
java.util.concurrent.ConcurrentLinkedQueue | java.util.LinkedList |
Another option to avoid ConcurrentModificationException is to use lock yourself. However, since there are built-in mechanisms available, using them would be suitable in most cases.
Conclusion
In this post, I’ve shown you what is ConcurrentModificationException and some methods to resolve the issues. You can either use lock or the thread-safe collections to avoid ConcurrentModificationException.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.