본문 바로가기
카테고리 없음

ConcurrentModificationException(동시 수정 오류) 설명 및 해결 방법

by daily_coming 2024. 9. 27.
반응형

 

ConcurrentModificationException(동시 수정 오류)란 무엇인가?

1. ConcurrentModificationException이란?

ConcurrentModificationException은 Java에서 컬렉션(Collection) 객체를 반복(iterate)하는 동안 해당 컬렉션이 다른 곳에서 수정되면 발생하는 런타임 예외입니다. 이 예외는 보통 하나의 스레드에서 컬렉션을 반복하면서 동시에 그 컬렉션을 수정할 때 발생합니다. Java의 컬렉션 클래스(List, Set, Map 등)는 기본적으로 한 번에 하나의 스레드만 안전하게 수정할 수 있도록 설계되어 있기 때문에, 동시 수정이 발생하면 ConcurrentModificationException이 발생합니다.

 

 

 

2. ConcurrentModificationException이 발생하는 이유

이 예외는 주로 다음과 같은 상황에서 발생합니다:

  • 반복 중 컬렉션 수정: 컬렉션 객체를 반복하는 동안 add(), remove() 메소드를 사용해 컬렉션의 요소를 추가하거나 삭제할 때 발생합니다.
  • 멀티스레딩 환경에서의 동시 수정: 두 개 이상의 스레드가 동일한 컬렉션을 동시에 수정하려고 할 때 발생할 수 있습니다.

 

 

 

 

3. ConcurrentModificationException 예시

3.1 기본적인 예시

아래 예시는 리스트를 반복하는 동안 리스트를 수정하려 할 때 발생하는 ConcurrentModificationException입니다.


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

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for (String item : list) {
            if (item.equals("B")) {
                list.remove(item);  // ConcurrentModificationException 발생
            }
        }
    }
}
    

위 코드에서는 리스트를 반복하면서 동시에 요소를 삭제하려고 하기 때문에 ConcurrentModificationException이 발생합니다.

 

3.2 멀티스레딩 환경에서의 예시

두 개 이상의 스레드가 동일한 리스트를 수정할 때도 이 예외가 발생할 수 있습니다.


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

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }

        // 첫 번째 스레드: 리스트를 반복
        new Thread(() -> {
            for (int item : list) {
                System.out.println(item);
            }
        }).start();

        // 두 번째 스레드: 리스트 수정
        new Thread(() -> {
            list.add(10);  // ConcurrentModificationException 발생 가능
        }).start();
    }
}
    

이 코드에서는 두 개의 스레드가 동시에 리스트를 반복 및 수정하면서 ConcurrentModificationException이 발생할 수 있습니다.

 

 

 

 

 

4. ConcurrentModificationException을 해결하는 방법

이 예외를 방지하기 위해 몇 가지 방법을 사용할 수 있습니다:

  • Iterator 사용 시 remove() 메소드 사용: 반복문 안에서 요소를 제거할 때는 컬렉션의 remove() 대신 Iteratorremove() 메소드를 사용해야 합니다.
  • CopyOnWriteArrayList 사용: CopyOnWriteArrayList와 같은 스레드 안전한 컬렉션을 사용하여, 동시 수정이 발생해도 예외가 발생하지 않도록 할 수 있습니다. 이 컬렉션은 수정 시 새로운 배열을 생성하여 안전한 동시성을 제공합니다.
  • 컬렉션 동기화: Collections.synchronizedList()를 사용하여 리스트를 동기화할 수 있습니다. 동기화된 컬렉션은 여러 스레드에서 안전하게 접근 및 수정할 수 있습니다.

4.1 Iterator의 remove() 메소드 사용

아래는 Iteratorremove() 메소드를 사용하여 ConcurrentModificationException을 방지하는 예시입니다:


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

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if (item.equals("B")) {
                iterator.remove();  // 안전한 삭제
            }
        }
    }
}
    

이 코드는 Iteratorremove() 메소드를 사용하여 안전하게 리스트에서 요소를 삭제할 수 있도록 합니다.

 

4.2 CopyOnWriteArrayList 사용

다음은 CopyOnWriteArrayList를 사용하여 동시 수정 예외를 방지하는 예시입니다:


import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Main {
    public static void main(String[] args) {
        List list = new CopyOnWriteArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for (String item : list) {
            if (item.equals("B")) {
                list.remove(item);  // 안전한 삭제
            }
        }
    }
}
    

CopyOnWriteArrayList는 컬렉션을 수정할 때 내부적으로 새로운 배열을 생성하여 동시 수정이 발생하지 않도록 설계된 클래스입니다.

 

 

 

 

 

5. 컬렉션 동기화

멀티스레드 환경에서 컬렉션을 수정하려면 동기화된 컬렉션을 사용하는 것이 좋습니다. 아래는 Collections.synchronizedList()를 사용한 예시입니다:


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

public class Main {
    public static void main(String[] args) {
        List list = Collections.synchronizedList(new ArrayList<>());

        // 스레드 1: 리스트 수정
        new Thread(() -> {
            list.add("A");
        }).start();

        // 스레드 2: 리스트 반복
        new Thread(() -> {
            synchronized (list) {
                for (String item : list) {
                    System.out.println(item);
                }
            }
        }).start();
    }
}
    

Collections.synchronizedList()는 동기화된 리스트를 제공하여 여러 스레드가 안전하게 리스트에 접근할 수 있도록 해줍니다.

 

 

 

 

6. 결론

ConcurrentModificationException은 Java에서 컬렉션을 반복하면서 동시에 수정할 때 발생하는 예외입니다. 이 예외를 해결하기 위해서는 Iteratorremove() 메소드를 사용하거나, 스레드 안전한 컬렉션을 사용해야 합니다. 또한, 멀티스레드 환경에서는 컬렉션을 동기화하여 예외 발생을 방지할 수 있습니다.

반응형