ConcurrentModificationException in Java

In this article, we will explore ConcurrentModificationException in Java in great details. We will understand causes of ConcurrentModificationException and examine measures to avoid ConcurrentModificationException in Single Threaded and Multi-threaded environments. Finally, we will look into the best practices.

ConcurrentModificationException in Java

What is ConcurrentModificationException in Java?

ConcurrentModificationException in Java is an exception that occurs in Java when attempting to modify a collection (such as a List, Set, or Map) while it is being iterated over using an iterator. It is an unchecked Exception.

This exception is designed to catch scenarios where the internal state of the collection has been changed by another thread or by the same thread, causing the iterator to become inconsistent with the collection’s modified state.

Causes of ConcurrentModificationException in Java

A ConcurrentModificationException in Java is typically caused by modifying a collection while it’s being iterated over using an iterator. Here’s a code example to illustrate this:

import java.util.ArrayList;
import java.util.List;

public class ConcurrentModificationExample {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
        myList.add("Item 1");
        myList.add("Item 2");
        myList.add("Item 3");

        // Attempt to modify the collection during iteration
        for (String item : myList) {
            myList.remove(item); // Causes ConcurrentModificationException
        }
    }
}

Output

Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
    at ConcurrentModificationExample.main(ConcurrentModificationExample.java:12)

In this example, a List called myList is created with three items. The code then attempts to iterate over the list using an enhanced for loop and remove each item from the list while iterating. This results in a ConcurrentModificationException because the collection is being modified during iteration.

To prevent this exception, you should avoid modifying a collection while iterating over it. Instead, use the iterator’s methods, such as remove(), to modify the collection safely. Here’s an example using the iterator to remove elements:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ConcurrentModificationFixedExample {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
        myList.add("Item 1");
        myList.add("Item 2");
        myList.add("Item 3");

        // Use iterator to remove elements during iteration
        Iterator<String> iterator = myList.iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove(); // Removes the current element safely
        }
    }
}

In this corrected example, the iterator’s remove() method is used to safely remove elements from the collection while iterating, preventing the ConcurrentModificationException.

Avoiding ConcurrentModificationException in Single Threaded Environment

In a single-threaded environment, avoiding ConcurrentModificationException is relatively straightforward. Since there are no other threads concurrently modifying the collection, you only need to be careful about modifying the collection while it’s being iterated. Here are a few approaches to avoid ConcurrentModificationException in a single-threaded environment:

Use an Iterator: When you need to modify a collection while iterating over it, use an explicit Iterator and its remove() method to safely remove elements. This way, the iterator and the collection remain in sync.

List<String> myList = new ArrayList<>();
myList.add("Item 1");
myList.add("Item 2");
myList.add("Item 3");

Iterator<String> iterator = myList.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if (shouldRemove(item)) {
        iterator.remove(); // Safely remove elements
    }
}

Use a Copy of the Collection:

Create a copy of the collection before iterating over it and perform modifications on the original collection. This prevents the iteration process from being affected by changes made during the iteration.

List<String> myList = new ArrayList<>();
myList.add("Item 1");
myList.add("Item 2");
myList.add("Item 3");

List<String> copyList = new ArrayList<>(myList);
for (String item : copyList) {
    if (shouldRemove(item)) {
        myList.remove(item); // Modify the original collection
    }
}

Use a Stream with Filter: If your intention is to remove specific elements from the collection, you can use Java Streams with the filter() operation to create a new list with the desired elements. This avoids modifying the original collection during iteration.

List<String> myList = new ArrayList<>();
myList.add("Item 1");
myList.add("Item 2");
myList.add("Item 3");

myList = myList.stream()
    .filter(item -> !shouldRemove(item))
    .collect(Collectors.toList()); // Create a new list without removed items

Avoiding ConcurrentModificationException in Multi-threaded Environment

In a multi-threaded environment, avoiding ConcurrentModificationException becomes more complex due to the potential for multiple threads concurrently modifying the collection. Proper synchronization and coordination are essential to prevent this exception. Here are some strategies to avoid ConcurrentModificationException in a multi-threaded environment:

Use Thread-Safe Collections: Java provides thread-safe collection classes in the java.util.concurrent package, such as CopyOnWriteArrayList, ConcurrentHashMap, and ConcurrentLinkedQueue. These collections are designed to handle concurrent modifications safely.

Synchronize Access: Use synchronization mechanisms like synchronized blocks or methods to ensure that only one thread can modify the collection at a time. This prevents concurrent modifications that can lead to the exception.

List<String> myList = Collections.synchronizedList(new ArrayList<>());

synchronized (myList) {
    // Modify myList safely
}

Use Locks: Utilize more advanced synchronization mechanisms like ReentrantLock or ReadWriteLock to control access to the collection. This allows for fine-grained control over read and write operations.

Iterate Safely with Copy: If you need to iterate over the collection and modify it, consider creating a copy of the collection for iteration. This ensures that the original collection isn’t modified during iteration.

Atomic Operations: If the modifications are simple and involve a single operation (e.g., adding an element), you can use atomic classes like AtomicReference to update the collection atomically.

Use Thread-Local Variables: If different threads need to maintain separate collections, consider using thread-local variables to store individual collections for each thread.

Use volatile or AtomicReference for Shared References: If multiple threads share a reference to a collection, use the volatile keyword or AtomicReference to ensure proper visibility of changes.

Use Concurrency Utilities: Java provides higher-level concurrency utilities, such as ExecutorService, to manage thread execution and coordination. Utilizing these can help avoid concurrency issues.

Best Practices

When working with collections in a multi-threaded environment, there are several best practices to follow to ensure proper synchronization, avoid exceptions like ConcurrentModificationException, and maintain the correctness of your program. Here are some key best practices:

  1. Choose Thread-Safe Collections: Whenever possible, use thread-safe collections from the java.util.concurrent package, such as CopyOnWriteArrayList, ConcurrentHashMap, and ConcurrentLinkedQueue. These collections are designed to handle concurrent modifications safely without additional synchronization.
  2. Use Explicit Synchronization: If you’re using standard collections like ArrayList, ensure proper synchronization using synchronized blocks or methods when reading and modifying the collection. This prevents multiple threads from accessing the collection simultaneously.
  3. Minimize Synchronized Blocks: Use synchronized blocks or methods only when necessary to avoid unnecessary contention and performance degradation. Keep the synchronized sections as short as possible.
  4. Separate Read and Write Operations: If multiple threads mostly read from the collection but rarely modify it, consider using a read-write lock (ReadWriteLock) to allow multiple threads to read concurrently while ensuring exclusive access during writes.
  5. Avoid Modifying While Iterating: Do not modify a collection while iterating over it using a traditional for-each loop or an iterator. Instead, use thread-safe collections or synchronize appropriately to avoid ConcurrentModificationException.
  6. Use Concurrent Data Structures: Choose the appropriate concurrent data structures based on your use case. For example, use ConcurrentHashMap for thread-safe maps, and ConcurrentLinkedQueue for thread-safe queues.
  7. Atomic Operations: Utilize atomic classes like AtomicInteger, AtomicLong, and AtomicReference when performing simple operations that need to be thread-safe. These classes provide atomic read-modify-write operations.
  8. Thread-Local Storage: When each thread needs its own copy of data, consider using thread-local variables. This prevents contention and eliminates the need for synchronization.
  9. Avoid Sharing Mutable State: Minimize sharing mutable objects between threads. Shared mutable state can lead to complex synchronization requirements and hard-to-debug issues.
  10. Use Concurrency Utilities: Utilize higher-level concurrency utilities like ExecutorService and ExecutorCompletionService to manage thread execution, coordination, and parallelism.

Conclusion: ConcurrentModificationException in Java

In this very comprehensive article on ConcurrentModificationException in Java, We explored in-depth on causes of ConcurrentModificationException, then looked into various ways of avoiding this kind of Exception in Single threaded and multi-threaded environments. Finally, we discussed, the best practices to follow to ensure proper synchronization, avoid exceptions like ConcurrentModificationException.

Read More : Common Unchecked Exceptions in Java

Leave a Reply

Your email address will not be published. Required fields are marked *