자바에서 컬렉션(Collection)을 순회(iteration)하는 동안 컬렉션에 변경이 일어나면 어떻게 될까요? 자바는 이 상황을 처리하는 두 가지 방식을 제공합니다. 바로 Fail-Fast(빠른 실패)와 Fail-Safe(실패 없음/안전 실패)입니다.
Fail-Fast 이터레이터는 순회 중 컬렉션 구조가 수정되면 즉시 ConcurrentModificationException을 던져 문제를 알립니다. 반면 Fail-Safe 이터레이터는 원본 컬렉션이 아닌 복제본(copy)을 대상으로 동작하기 때문에 순회 중에도 수정이 가능하며 예외를 발생시키지 않습니다.
Fail-Fast와 Fail-Safe 비교표
| 번호 | 구분 | Fail-Fast | Fail-Safe |
|---|---|---|---|
| 1 | 예외 발생 여부 | 스레드가 컬렉션을 순회하는 도중 추가, 삭제, 수정 등 컬렉션에 변화가 생기면 ConcurrentModificationException을 던집니다. | 컬렉션의 복사본을 사용하므로 예외를 발생시키지 않습니다. |
| 2 | 대표 컬렉션 | ArrayList, HashMap 등이 Fail-Fast 이터레이터의 대표적인 예입니다. | CopyOnWriteArrayList, ConcurrentHashMap 등이 Fail-Safe 이터레이터의 대표적인 예입니다. |
| 3 | 성능과 메모리 | 실제 컬렉션 위에서 직접 동작하므로 추가적인 메모리와 시간이 필요하지 않습니다. | 실제 컬렉션이 아닌 복제본 위에서 동작하므로 시간과 메모리 측면에서 오버헤드가 발생합니다. |
| 4 | 수정 허용 여부 | 순회 중에는 컬렉션의 수정을 허용하지 않습니다. | 순회 중에도 컬렉션의 수정을 허용합니다. |
Fail-Safe 예제 코드
아래 예제는 ConcurrentHashMap을 사용한 Fail-Safe 동작을 보여줍니다. 순회 도중 새로운 요소를 추가해도 예외가 발생하지 않습니다.
public class FailSafeExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
// 맵에 요소 추가
map.put("Dell", 1);
map.put("IBM", 2);
// 맵에서 이터레이터 가져오기
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
System.out.println(key + " : " + map.get(key));
map.put("Google", 3); // 순회 중 수정해도 예외 없음
}
}
}실행 결과
IBM : 2 Dell : 1
Fail-Fast 예제 코드
반면 아래 예제는 ArrayList를 사용한 Fail-Fast 동작을 보여줍니다. 순회 도중 요소를 추가하려 하면 ConcurrentModificationException이 발생합니다.
public class FailFastExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
// 리스트에서 이터레이터 가져오기
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer integer = (Integer) it.next();
list.add(4); // 순회 중 수정 → 예외 발생!
}
}
}실행 결과
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)정리
Fail-Fast는 데이터 무결성을 빠르게 확인할 수 있어 단일 스레드 환경에서 유용하고, Fail-Safe는 멀티스레드 환경에서 안전하게 컬렉션을 다룰 수 있다는 장점이 있습니다. 다만 Fail-Safe는 복제본을 생성하는 비용이 들기 때문에 성능과 메모리 사용량을 고려하여 상황에 맞게 선택하는 것이 중요합니다.