Sunday, 30 August 2026

CONCURRENTMODIFICATION EXCEPTION

 Yes. The important thing here is that **`ConcurrentModificationException` can happen even with only ONE thread**. The word "Concurrent" is a little misleading.


# Why does `ConcurrentModificationException` happen?


Java collections such as `ArrayList` use **fail-fast iterators**.


That means:


> While an iterator is traversing a collection, don't structurally modify the collection directly. If you do, the iterator detects it and may throw `ConcurrentModificationException`.


---


## Simple example


```java

List<String> list = new ArrayList<>();


list.add("A");

list.add("B");

list.add("C");


for (Iterator<String> it = list.iterator(); it.hasNext();) {


    String value = it.next();


    if (value.equals("B")) {

        list.remove(value);   // ❌ Problem

    }

}

```


What is happening?


```text

List

┌─────────────┐

│ A │ B │ C   │

└─────────────┘

      ↑

   Iterator

```


The iterator is keeping track of the collection's state.


Then you do:


```java

list.remove("B");

```


You changed the collection **behind the iterator's back**.


```text

Iterator thinks:


A → B → C


But collection changed:


A → C

```


The iterator detects that the collection was structurally modified and can throw:


```text

ConcurrentModificationException

```


---


# But there is only ONE thread! πŸ˜•


Exactly!


```text

Thread 1

   │

   ├── Iterator

   │

   └── list.remove()

```


There is no second thread.


Still, you can get:


```text

ConcurrentModificationException

```


Because **"concurrent" here means the collection was modified while an iterator was in progress**, not necessarily that two threads are involved.


---


# How does Java detect it?


Conceptually, `ArrayList` maintains a modification count:


```text

modCount

```


When you structurally modify the list:


```java

list.add(...)

list.remove(...)

```


the modification count changes.


When an iterator is created, it remembers the current count:


```text

Collection:


modCount = 3


Iterator:

expectedModCount = 3

```


Then:


```java

list.remove("B");

```


Collection becomes:


```text

modCount = 4

```


But iterator still has:


```text

expectedModCount = 3

```


When the iterator calls `next()`:


```text

expectedModCount != modCount

        ↓

      ERROR

        ↓

ConcurrentModificationException

```


This is the basic idea behind the fail-fast behavior.


---


# ✅ Correct way: use `Iterator.remove()`


If you're already using an iterator, let the **iterator itself remove the element**.


```java

for (Iterator<String> it = list.iterator(); it.hasNext();) {


    String value = it.next();


    if (value.equals("B")) {

        it.remove();   // ✅ Correct

    }

}

```


Why does this work?


Because the iterator knows that it is doing the removal and updates its internal state appropriately.


```text

Iterator

   │

   ├── next()

   │

   └── remove()

         ↓

   Collection modified

         ↓

   Iterator knows about it ✅

```


---


# What about multi-threading?


Now suppose:


```text

Thread 1                    Thread 2

--------                    --------

Iterator

   │

   │

   └──────────────────────> list.remove()

```


Thread 2 modifies the list while Thread 1 is iterating.


Thread 1's iterator can detect the modification and throw:


```text

ConcurrentModificationException

```


So there are **two situations**:


### Single thread


```text

Thread 1

   │

   ├── iterator.next()

   ├── list.remove()  ❌

   └── iterator.next()

              ↓

   ConcurrentModificationException

```


### Multiple threads


```text

Thread 1                 Thread 2

--------                 --------

iterator.next()

                         list.remove()

iterator.next()

     ↓

ConcurrentModificationException

```


---


# What about `Collections.synchronizedList()`?


This is another important interview point.


You might think:


```java

List<String> list =

    Collections.synchronizedList(new ArrayList<>());

```


means everything is automatically safe.


**Not quite.**


Individual operations are synchronized:


```java

list.add("A");       // thread-safe

list.remove("A");    // thread-safe

list.get(0);         // thread-safe

```


But iteration is a **compound operation**.


You should synchronize the entire iteration:


```java

synchronized (list) {


    Iterator<String> it = list.iterator();


    while (it.hasNext()) {

        System.out.println(it.next());

    }

}

```


The important idea is:


> **Making individual operations thread-safe does not automatically make a sequence of operations thread-safe.**


---


# Easy example of compound operation


Imagine:


```java

if (!list.contains("A")) {

    list.add("A");

}

```


You might think this is safe because both methods are synchronized.


But:


```text

Thread 1                    Thread 2

--------                    --------

contains("A") → false

                            contains("A") → false

add("A")

                            add("A")

```


Now `"A"` can be added twice.


Why?


Because:


```text

contains()

   ↓

decision

   ↓

add()

```


is actually **one logical operation**, but synchronization of individual methods doesn't protect the whole sequence.


---


# 🧠 Best way to remember


### `list.remove()` during iterator traversal ❌


```java

Iterator → next()

List     → remove()   ❌

```


### `iterator.remove()` ✅


```java

Iterator → next()

Iterator → remove()   ✅

```


### Multi-threading


```text

One thread modifies

       +

Another thread iterates

       ↓

ConcurrentModificationException

```


### But remember:


```text

ConcurrentModificationException

          ≠

Only multiple threads

```


It can happen with **one thread too**.


---


## 🎯 Interview answer


> **Java collection iterators are generally fail-fast. If a collection is structurally modified after an iterator is created, except through the iterator's own `remove()` method, the iterator may detect the modification and throw `ConcurrentModificationException`. This can happen even in a single-threaded program. With multiple threads, one thread modifying the collection while another is iterating can cause the same problem. Synchronized collection wrappers make individual operations thread-safe, but compound operations and iteration may still require external synchronization.**


### One-line memory trick:


**"If an Iterator is walking the collection, don't change the collection directly—change it through the Iterator."**



--------------

Yes. For multiple threads, the solution depends on what you need: simple synchronization, a concurrent collection, or an iterator designed for concurrent modification.

Let's use the same example.

List<String> list = new ArrayList<>();

list.add("A");
list.add("B");
list.add("C");

Suppose:

Thread 1 → iterates list
Thread 2 → removes/adds elements

1. Simple solution: synchronize the whole operation πŸ”’

If you use:

Collections.synchronizedList(...)

you must synchronize the entire iteration.

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

synchronized (list) {

    Iterator<String> it = list.iterator();

    while (it.hasNext()) {
        String value = it.next();
        System.out.println(value);
    }
}

And when another thread modifies it:

synchronized (list) {
    list.remove("B");
}

Now:

Thread 1                    Thread 2
--------                    --------
lock list
iterate
iterate
iterate
unlock
                            lock list
                            remove()
                            unlock

Only one thread accesses the critical section at a time.

When to use?

When you want strong consistency and the collection isn't extremely performance-sensitive.


2. Better for concurrent access: CopyOnWriteArrayList

If you have:

Many readers + very few writers

use:

CopyOnWriteArrayList<String> list =
    new CopyOnWriteArrayList<>();

Example:

CopyOnWriteArrayList<String> list =
    new CopyOnWriteArrayList<>();

list.add("A");
list.add("B");
list.add("C");

Thread 1:

for (String value : list) {
    System.out.println(value);
}

Thread 2:

list.remove("B");

No ConcurrentModificationException.

Thread 1                    Thread 2
--------                    --------
iterate
                            remove B
iterate
iterate

The iterator works on a snapshot of the list.

So Thread 1 might see:

A B C

even though Thread 2 removes B.

After the modification, a new iterator sees:

A C

Important:

CopyOnWriteArrayList is expensive for frequent writes because every modification creates a new underlying array.

So:

Many reads + few writes → ✅ CopyOnWriteArrayList
Many writes              → ❌ Usually not ideal

3. Use ConcurrentHashMap for maps

If your collection is a map:

Map<String, Integer> map =
    new ConcurrentHashMap<>();

Then multiple threads can safely work with it.

ConcurrentHashMap<String, Integer> map =
    new ConcurrentHashMap<>();

map.put("A", 100);
map.put("B", 200);

Thread 1:

for (String key : map.keySet()) {
    System.out.println(key);
}

Thread 2:

map.remove("A");

ConcurrentHashMap provides weakly consistent iterators.

Unlike ArrayList's fail-fast iterator, the iterator doesn't throw ConcurrentModificationException just because another thread modifies the map.


4. If you need a producer-consumer scenario → BlockingQueue

For many multi-threaded designs, instead of sharing an ArrayList, use a concurrent queue.

Example:

BlockingQueue<String> queue =
    new LinkedBlockingQueue<>();

Producer:

queue.put("Order-1");

Consumer:

String order = queue.take();

Architecture:

Producer Threads
      │
      ↓
┌───────────────┐
│ BlockingQueue │
└───────┬───────┘
        ↓
 Consumer Threads

This is often much cleaner than multiple threads directly modifying an ArrayList.


Which one should you choose?

SituationSolution
Need simple protectionsynchronized
Existing ArrayList, readers/writersSynchronize access
Many reads, few writesCopyOnWriteArrayList
Concurrent MapConcurrentHashMap
Producer/consumerBlockingQueue
Need atomic operationsAtomic classes / synchronization

⭐ Most important interview distinction

Don't say:

"ConcurrentModificationException means the collection isn't thread-safe."

That's incomplete.

Instead say:

"ConcurrentModificationException is a fail-fast behavior of many collection iterators. In a multi-threaded environment, I can avoid the problem by protecting iteration and modification with the same lock, or by using an appropriate concurrent collection such as CopyOnWriteArrayList or ConcurrentHashMap depending on the access pattern."

🧠 Remember:

ArrayList
   ↓
Multiple threads modifying/iterating
   ↓
Need external synchronization
             OR
Use appropriate concurrent collection

And one very important point:

Don't simply catch ConcurrentModificationException and continue.
The correct solution is to design the access pattern so the unsafe concurrent modification doesn't occur.

No comments:

Post a Comment