Guide to ConcurrentModificationException

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 CollectionNon-thread-safe Counterpart
java.util.concurrent.CopyOnWriteArrayListjava.util.ArrayList
java.util.concurrent.CopyOnWriteArraySetjava.util.HashSet
java.util.concurrent.ConcurrentHashMapjava.util.HashMap
java.util.concurrent.ConcurrentSkipListMapjava.util.TreeMap
java.util.concurrent.ConcurrentLinkedQueuejava.util.LinkedList
Thread-safe and non thread-safe collections in Java

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.

Leave a Comment