[STS]자바에서 Collection 수정 중 ConcurrentModificationException 에러

cielo ru·2024년 8월 28일
0

Spring

목록 보기
9/9
post-thumbnail

➰ 이슈

자바에서 MVC 구조를 활용한 미니 프로젝트를 진행하던 중 java.util.ConcurrentModificationException 예외가 발생했다.

에러

for (Animal animal : searchAnimals) {
		if(animal.getPetId() != animalPK) {
			searchAnimals.remove(animal);
		}
}
        

에러 메시지

 java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
	at ubis.service.PetChargerService.getAnimalList(PetChargerService.java:75)
	at ubis.controller.PetChargerController.getAnimalList(PetChargerController.java:55)
	at ubis.view.StartView.main(StartView.java:79)

➰ 원인

ConcurrentModificationException동시 수정 예외로, List, Map과 같은 컬렉션을 수정하는 도중에 다른 스레드에서 동시에 컬렉션을 수정하려고 발생하는 예외이다.

수정?

  • 요소를 추가(add)
  • 요소를 삭제(remove)
  • 컬렉션의 순서를 변경
for (Animal animal : searchAnimals) {
		if(animal.getPetId() != animalPK) {
			searchAnimals.remove(animal);
		}
}

현재 forEach() 루프가 내부적으로 Iterator를 사용하여 컬렉션을 반복하고 있다. Iterator는 반복하는 동안 내부에 반복자가 돌아가고 있고 중간에 다른 스레드에서 컬렉션을 수정하려고 할 때 이를 감지하고 ConcurrentModificationException을 던진다.

즉, getAnimalList 메소드에서 searchAnimals 리스트를 반복하는 동안 remove 메소드를 호출해서 이런 예외가 발생했다.

🌱 ConcurrentModificationException이 발생하는 근본적인 이유?

반복자(Iterator)의 안전성(safety)을 보장하기 위해서

📍 반복자의 안전성을 보장해야 하는 이유?

컬렉션의 구조적 변경은 반복자(Iterator)와의 일관성을 깨트린다.
컬렉션의 구조가 반복 중에 변경되면, 반복자의 동작이 예측할 수 없게 된다. 예를 들어, 요소가 추가되거나 삭제된 후 반복자가 어떤 요소를 반환할지 확신할 수 없다. 또한 반복 중 컬렉션의 구조가 변경되면, 잘못된 데이터를 읽어오거나 프로그램이 비정상적으로 동작할 수 있다. 그래서 애초에 ConcurrentModificationException 예외를 통해 이러한 위험을 방지한다.

즉, ConcurrentModificationException 예외는 자바 컬렉션 프레임워크가 동시 수정으로 인한 데이터 불일치를 방지하기 위해 고안된 메커니즘이라고 이해하면 된다.

➰ 해결

1) removeIf 사용

searchAnimals.removeIf(animal -> animal.getPetId() != animalPK);

Stream API 의 removeIf 메소드를 사용하여 조건에 맞는 요소를 일괄적으로 삭제했다. 이 메소드는 내부적으로 안전하게 작업을 처리하기 때문에 에러가 발생하지 않는다.

2) Iterator 사용

Iterator<String> iterator = searchAnimals.iterator();
while (iterator.hasNext()) {
    String animal = iterator.next();
    if (animal.equals("Lion")) {
        iterator.remove(); // Iterator의 remove 메소드 사용
    }
}

Iterator의 remove() 메소드를 사용하여ConcurrentModificationException을 방지한다. 이 메소드는 반복자와 컬렉션의 상태를 일치시켜 준다.

➰ Mini Project

아래는 위 에러가 발생하면서 진행했던 프로젝트이다.

MVC 구조와 Stream API를 익히고자 진행하였다.

😃 [UBIS] MVC Mini Project

➰ 참고

profile
Cloud Engineer & BackEnd Developer

0개의 댓글