개발 As 공부/JAVA

[JAVA] ConcurrentModificationException 해결 썰

민킹 2022. 3. 24. 21:27

 

프로그래머스의 '주차 요금 계산'문제를 풀고 코드 제출을 했다. 16개의 테스트 케이스 중 대부분의 케이스가 '런타임에러'를 띄웠다.

처음에는 얼핏 빨간색만 보고 로직 문제로 인한 일반적인 실패인 줄 알았다. 근데 자세히 보니 런타임 에러? 정확히 어떤 에러인지 알려주지는 않는 것 같았다. 그냥 다른 로직을 짜야 하나 고민 하던 중 어쨌든 지금 내가 제출한 코드가 제대로 된 코드인지 확인하고 싶었다. 그러려면 일단 런타임 에러를 해결해야 했다.

 

코드의 로직은 크게 세 부분으로 나뉜다(메인 함수에 로직이 세 개). 우선 확인 범위를 줄이기 위해 어떤 로직에서 에러가 나는지 확인 해 볼 것이다.

 

첫 번째 로직을 주석 처리 해 보았다. 런타임 에러가 떴다. 그럼 첫 번째 로직에서 런타임 에러가 나고 있을 확률이 적다.

다음으로 두 번째 로직만 주석처리를 했다. 

 

두 번째 로직 주석처리 결과: 런타임 에러X

런타임 에러가 아닌 그냥 일반적인 실패가 뜬다(정답이 틀린 경우다). 그럼 두 번째 로직이 런타임 에러를 내고 있을 확률이 높은걸로 보인다. 어딘가에서 문법적 오류가 발생하고 있는 것 같다.

마지막으로 세 번째 로직만 주석처리를 해 주었다. 첫 번째 경우처럼 런타임에러가 뜬다.

 

그럼 이제 해야 할 것은? 두 번째 로직이 에러를 내는 경우의 수를 찾아내는 것이다.

 

두 번째 로직 코드 보기

간단히 설명을 하자면 Map안에 저장 된 값을 하나씩 불러와 연산처리를 해 주는 로직이다. 다행히 코드 자체가 길지 않아 확인 할 부분이 적겠다.

 

임의로 테스트 케이스를 만들어서 테스트 해 보아야 한다. 일단 예제에 있던 경우는 모두 통과 했으니 예제에 없던 경우를 찾아 테스트 케이스를 만들어야 겠다. 케이스 서너 개 쯤을 시도 해 보다가 드디어 에러를 내는데 성공했다! 에러명은 'ConcurrentModificationException'이었다.

 

아.....!

 

ConcurrentModificationException이 발생하는 이유에 대해 검색을 하면 "어떤 쓰레드가 Iterator로 반복중인 Collection을 수정하는 경우 발생" 한다고 나온다. 말 그대로 나는 해당 로직 맨 끝 부분에서 key와 value를 지워주고 있었다. 

 

해당 부분을 주석 처리 해 주고 다시 코드를 제출했다.

 

통과 했다! 이전에 뜨던 런타임 에러는 'ConcurrentModificationException'이 맞았나 보다.

 

가끔 코드를 치다가 나도 모르게 생각보다 손이 먼저 움직이는 경우가 있다. 오늘같은 경우가 딱 그런 경우다. 사실 런타임 에러가 뜨길래 'NullPointerException'일거라 생각하고 Integer unwrapping부분을 봐야하나 생각 했는데 의외로 다른 에러라서 좀 재밌었던 것 같다.

 

결론

Iterator를 사용해 Collection을 순회 할 때는 'concurrency'를 헤치지 않도록 주의 하자!