JAVA ConcurrentModificationException

Kevin·2024년 3월 25일
0

Trouble Shooting

목록 보기
3/7
post-thumbnail

서론

평상시와 다름없이 개발을 하던 도중 For문을 통해서 컬렉션의 원소들을 각각 순회하면서, 해당 원소를 제거하는 로직을 구현하였다.

그렇게 구현을 하고, 실행을 시켜보니 아래와 같은 ERROR LOG를 보게 되었다.

java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.aimir.nondemand.ModmProcService.execute(ModmProcService.java:95)
	at com.aimir.nondemand.ModmProcJob.executeInternal(ModmProcJob.java:22)
	at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:112)
	at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)

ConcurrentModificationException 예외가 과연 어떤 문제로 인해서 터지게 된 것인지 알아보자.


ConcurrentModificationException

아래는 내가 작성했던 코드의 일부분을 각색한 코드이다.

	public void execute() throws SQLException, InterruptedException, IOException {

	 ArrayList<VO> VOs = new ArrayList<VO>>();
	    
	    if (VOs.size() <= 0) {
	    		break;
	    	}
	    	
	    for (VO vo : VOs) {
	    
		    if (VOs.remove(vo)) {
		        System.out.println("정상적으로 삭제했습니다.");
		     }
		     
		  }
	          
	}	    

ConcurrentModificationException 예외는 일반적으로 컬렉션을 순회하면서, 순회하는 대상 컬렉션에 수정을 가할 경우에 발생한다.

즉 위의 경우에 컬렉션 VOs를 순회하면서, 순회하는 대상 컬렉션인 VOs에 수정을 가하고 있기에 예외가 발생한 것이다.

컬렉션 객체에는 컬렉션의 Element, 원소가 추가되거나 제거될 때 마다 컬렉션 내의 modCount 변수를 수정하는 동작이 추가되어있다.

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

위는 컬렉션에서 remove시마다 내부적으로 수행되는 메서드이다.

여기서 다루는 modCount 변수는 컬렉션의 구조가 수정된(크기가 수정된) 횟수를 나타내는 변수이다.

그리고 컬렉션을 순회하는 next() 메서드에서는 순회가 시작할 때의 초기 컬렉션의 modCount와 현재 순회 중인 modCount를 비교하는 동작이 있는데, 이 때 두 값이 다르면 ConcurrentModificationException 예외가 발생한다.

final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

위 코드는 컬렉션의 next() 메서드에서 내부적으로 호출되는 modCount를 비교하는 메서드이다.

그러면 어떻게 해야하나?

이 경우에서는 컬렉션에 직접적으로 수정을 하는게 아니라, iterator를 통해서 삭제하면 된다.

그러면 modCount와 관련된 문제가 발생하지 않기에, 예외 또한 발생하지 않을 것이다.

이러한 이유로 컬렉션 프레임워크에서 컬렉션 원소의 값을 가져오거나 삭제할 때 modCount 문제를 피하기 위해서 Iterator를 많이 사용한다고 한다.


아래는 변경된 코드이다.

	public void execute() throws SQLException, InterruptedException, IOException {

	 ArrayList<VO> VOs = new ArrayList<VO>>();
	 
	 Iterator<VO> iterator = VOs.iterator();
	    	    	
	 while (iterator.hasnext()) {
	    VO vo = iterator.next();

			iterator.remove();
		     
		}
	          
	}	    
profile
Hello, World! \n

0개의 댓글