You are currently viewing How To Fix ConcurrentModificationException In Java

How To Fix ConcurrentModificationException In Java

1. Introduction

When dealing with Collections in Java, ConcurrentModificationException is an exception you’ll most probably encounter. In this article, you’ll learn how to fix this exception. You’ll also learn some best practices for avoiding the ConcurrentModificationException.

2. What Java Says about ConcurrentModificationException

According to the Javadoc, a ConcurrentModificationException is thrown to indicate an attempt to perform a concurrent modification of an object when such operation is not permissible. One common scenario is when a thread tries to modify a Collection while another thread is iterating over it. Another situation is when a single thread attempts to issue a sequence of method invocations that violates the contract of an object.

3. How to Reproduce

3.1. In a Multithreaded Environment

Let’s consider the following code which consists of two threads: the main thread and the simpleThread:

class Scratch {

    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        for(int i=0; i<10; i++){
            list.add("hello");
        }
        Thread simpleThread = new Thread(new SimpleThread(list));
        simpleThread.start();
        for(String value: list){//ConcurrentModificationException due to ListIterator#next() method
            System.out.println(value);
        }
    }    
}

class SimpleThread implements Runnable{
    private List<String> list;
    public SimpleThread(List list){
        this.list = list;
    }

    @Override
    public void run() {
        this.list.add("world");
    }
}

Running the above program will randomly throw a ConcurrentModificationException.

hello
hello
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
    at Scratch.main(scratch_2.java:19)

Depending on the configuration of your workstation, the exception might be thrown after 2, 3, or more iterations of the for loop. The exception might never be thrown as well. If you want to ensure that the exception is always thrown, just increase the size of list so the loop doesn’t complete before the simpleThread starts.

        for(int i=0; i<100; i++){//Increase here
            list.add("hello");
        }

The exception is thrown here because we are trying to call the next() method of the ListIterator on a List that has been updated after the Iterator was created.

How to Fix the Problem
A basic solution to this problem is to use an array instead of an ArrayList. This solution is only acceptable if you are dealing with small or medium size arrays as you will lose all the advantages of the Collection API.

class Scratch {
    public static void main(String[] args) throws Exception {
        String[] list = new String[10];
        for(int i=0; i<list.length; i++){
            list[i] = "hello";
        }
        //Adapt the remaining code accordingly
    }
}

Another workaround is to use an index while looping on the list, instead of the enhanced for loop which automatically creates an Iterator.

class Scratch {
    public static void main(String[] args) throws Exception {
        //Remaining code is unchanged
        Thread simpleThread = new Thread(new SimpleThread(list));
        simpleThread.start();
        for(int i=0; i < list.size(); i++){//No ConcurrentModificationException as we are not using ListIterator
            System.out.println(list.get(i));
        }
    }
}

A more recommendable approach is to use a thread-safe implementation of ArrayList, namely CopyOnWriteArrayList.

class Scratch {
    public static void main(String[] args) throws Exception {
        List<String> list = new CopyOnWriteArrayList<>();//Update here
        for(int i=0; i<100; i++){
            list.add("hello");
        }
        Thread simpleThread = new Thread(new SimpleThread(list));
        simpleThread.start();
        for(String value: list){//No ConcurrentModificationException
            System.out.println(value);
        }
    }
}

When using CopyOnWriteArrayList, the Iterator is always created on a fresh copy of the List.This means that any changes made on the list will not reflect on the iterator itself.

3.2. In a Singlethreaded Environment

Let’s consider the following code snippet, where we are looping over a List of Strings.

        List<String> list = new ArrayList<>();
        list.add("hello");
        for(String value : list){// ConcurrentModificationException
            System.out.println(value);
            list.add("world");
        }

Any attempt to add a new element to the list within the loop body raises a ConcurrentModificationException.

Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
    at Scratch.main(scratch_2.java:15)

Obviously, the same situation will happen if you try to remove an element instead of adding it. Just like we saw earlier in a multithreaded environment, the problem occurs because the ListIterator tries to call the next() method on a List that has been updated after the iterator was created.

How to Fix the Problem

We only focus here on solutions that use the Collection API.

Use an Index-based for loop
As we saw earlier, an index-based for loop does not use an Iterator under the hood.

class Scratch {
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("hello");
        for(int i=0; i< list.size(); i++){// No ConcurrentModificationException
            System.out.println(list.get(i));
            list.add("world");
        }
    }
}

Use a Concurrent Collection
The CopyOnWriteArrayList is a safe alternative when you plan to update your ArrayList from different threads. You need to be aware that the list changes made after the iterator is created will not be reflected.

class Scratch {
    public static void main(String[] args) throws Exception {
        List<String> list = new CopyOnWriteArrayList<>();
        list.add("hello");
        for(String value: list){// No ConcurrentModificationException
            //Any changes made by at this point will not reflect on this loop
            System.out.println(value);
            list.add("world");//This will not be printed
        }
    }
}

4. Conclusion

In this brief tutorial, you learned about the ConcurrentModificationException and how to fix it.

You can find the complete code of this article here in GitHub

Noel Kamphoa

Experienced software engineer with expertise in Telecom, Payroll, and Banking. Now Senior Software Engineer at Societe Generale Paris.