JAVA

[JAVA] iterator 순회 중 만난 ConcurrentModificationException

경딩 2024. 11. 20. 15:19
  • collection 순회하는 loop 내에서 원본 collection에 변화를 주면 어떤 일이 발생할까?

 

다음 코드를 실행해보자!

package generic.ex1;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class RawTypeMain {
    public static void main(String[] args) {

   //   List<String> list2 = new CopyOnWriteArrayList<>();
        List<String> list2 = new ArrayList<>();
        list2.add("A");
        list2.add("B");
        list2.add("C");
        list2.add("D");



        System.out.println("Before: " + list2);

        Iterator<String> iterator = list2.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if ("A".equals(item)) {
                list2.remove(item); // ConcurrentModificationException 발생
            }
        }

        System.out.println("After: " + list2);

    }
}

 

실행결과를 보면 ConcurrentModificationException 이 발생하는 것을 확인할 수 있다.

 

 

ArrayList에 내부 로직을 보면 Interator 생성시점에 expectedModCount에 modCount를 저장하는 것을 확인할 수 있다.

 

 

ArrayList 내부에서 modCount 기본값은 0으로 초기화된다.

add(), remove(), clear() 같은 메서드가 호출되면 modCount 값이 증가한다.

 

 

 

iterator 내부 메서드에서 checkForComodification 부분에서 expectedCount와 modCount 가 같지 않을 경우 

ConcurrentModificationException 에러를 던지는 것을 확인할 수 있다.

 

이와 같이 바로 에러를 던지는 방법을 Faill-fast 동작이라 하며, 순회 중 구조적 수정이 감지되면 예외를 던진다.

해당 문제를 해결하기 위해 CopyOnWriteArrayList 컬렉션을 쓰면 순회 중 안정하게 수정이  가능하다.

 

자바의 대부분 컬렉션 프레임워크는 스레드 세이프하지 않다.

쓰레드 세이프란? 여러 메서드가 동시에 접근하여 수정하여도 프로그램이 의도한 대로 동작하도록 보장되는 상태를 말한다.

 

 

스레드 세이프 관련 개념과 사용 가능한 클래스들(예: ConcurrentHashMap, CopyOnWriteArrayList)을 공부하면 좋다.

  1. Concurrent Collection은 멀티스레드 환경에서 성능과 안정성을 동시에 제공한다.