컬렉션 프레임워크에서 런타임 예외를 보다가 예전에 게임을 만들면서 발생했던 ConcurrentModificationException이 있어서 공부해보기로 했다.
그 때 당시 배열을 조회하는 도중에 추가 및 삭제가 발생되어서 생기는 예외라고 들어서 iterator의 remove()를 사용하여 삭제는 해결했고,
삽입같은 경우는 딜레이를 줘서 예방했던 기억이 있다.
💻 먼저 설명하기 전에 iterator가 무엇인지 알아보자.
자바 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소를 읽어오는 방법을 표준화했는데 그것이 Iterator이다.
그래서 컬렉션 인터페이스에는 Iterator를 구현한 클래스의 인스턴스를 반환하는 iterator()가 있다.
🤔 ConcurrentModificationException이란?
iterator가 컬렉션을 반복하고 있는데 컬렉션 요소에 대한 추가 및 삭제가 발생해서 생기는 예외이다.
(RuntimeException에 속한다.)
java 8 API 공식문서에는 다음과 같이 정의했다.
ConcurrentModificationException
Thrown by iterators and list iterators if the backing collection is changed unexpectedly while the iteration is in progress. Also thrown by sublist views of lists if the backing list is changed unexpectedly.
iteration(반복)이 진행되는 동안 backing collection이 예기치 않게 변경될 경우 iterators와 list iterators에 의해 발생된다.
또한 backing list가 예기치 않게 변경될 경우 lists의 sublist views에 의해 발생된다.
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
수정이 불가능한 object의 동시 수정이 발견된 메서드에 의해 이 exception이 발생할 수 있다.
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.
예를 들어, 다른 스레드가 iterating을 수행하는 동안 컬렉션을 수정하기 위한 단일 스레드는 일반적이지 않다.
In general, the results of the iteration are undefined under these circumstances.
일반적으로, iteration의 결과는 이러한 상황에서 정의되지 않습니다.
Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected.
일부 iterator 구현체(JRE가 제공하는 모든 범용 수집 구현체 포함)는 이 동작이 감지되면 이 예외를 발생시키기로 선택할 수 있다.
Iterators that do this are known as fail-fastiterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.
이를 반복하는 반복자는 미래에 결정되지 않은 시간에 임의의 비결정적 행동을 감수하는 대신 빠르고 깨끗하게 실패하기 때문에 fail-fast iterators로 알려져 있다.
요약하자면 수정 불가능한 객체에 동시 수정(concurrent modification)이 메서드에 의해 발견되면 ConcurrentModificationException 예외를 발생함.
예를 들면, Iterator가 컬렉션을 반복하는 중에 이 컬렉션에 대한 수정이 일어날 때 Fail-Fast이면 ConcurrentModificationException 예외를 발생함.
🤔 Fail-Fast Iterator와 Fail-Safe Iterator는 무엇인가요?
Fail-Fast Iterator 는 오류가 발생하면 빠르게 오류를 알리고 작업을 중지시킨다.
Fail-Safe Iterator는 컬렉션의 사본을 쓰기 때문에 오류가 발생해도 작업을 중단하지 않고 진행하는 것이다.
🤔 그럼 어떤게 Fail-Fast이고 Fail-Safe 방식인거죠?
java.util.concurrent 패키지의 컬렉션들은 Fail-Safe이다.
그 외의 java.util 패키지의 컬렉션들은 모두 Fail-Fast 방식이다.
🤔 그럼 해결방법은 어떤것이 있나요?
1. Fail-Safe 방식의 컬렉션인 ConcurrentHashMap이나 CopyOnWriteArrayList 등을 사용한다면 이 예외를 예방할 수 있다.
2. Iterator.remove() 를 사용하여 컬렉션 요소를 삭제한다. 이것은 반복 중에 컬렉션을 조작할 수 있는 안전한 방법이다.
3. 컬렉션 요소를 삭제말고도 추가도 하고 싶다면 ListIterator를 사용하여 add 또는 remove를 사용하면 된다.
이렇게 ConcurrentModificationException에 대해 자세히 공부해보았다.
예전에 게임을 만들면서 발생했던 예외지만 너무 급해서 어떻게든 예방만 하여 넘어갔던 부분인데
지금 공부하고 살펴보니 ListIterator를 사용해서 컬렉션 요소에 대한 추가와 삭제를 했었어야 했다.
'프로그래밍 > 예외,에러' 카테고리의 다른 글
Swagger 3.0 documentationPluginsBootstrapper 에러 해결하기 (0) | 2023.05.03 |
---|---|
스프링부트 실행 시 IllegalStateException 발생 (0) | 2023.02.13 |
Java ArrayIndexOutOfBoundsException (0) | 2022.01.23 |