Environment

  • JDK 1.8 (1.2부터 추가)

 

java.util.concurrentmodificationexception 연구실에서 무난한 Java Application을 구현하는 중에 맞딱뜨린 Exception…

List나 Map 등 Collection을 순회하는 도중에 해당 객체를 변경하면 발생할 수 있는 Runtime Exception입니다. 공식문서에 따르면, 한 스레드가 Collection을 순회(iterate)하고 있을 때, 다른 스레드가 그 Collection을 변경하게 되면 발생한다고 합니다.(it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.)
특이한 사항은 이 Exception은 서로 다른 스레드가 아닌, 한 스레드에 대해서도 발생할 수 있다. (If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception.)

그래서 연구실에서 구현한 아래의 Wrong Code 코드가 concurrentmodificationexception을 발생시킨 것.

아래 코드의 목적은 특정 아이피가 몇 번이상 업데이트되지 않으면, 해쉬에서 원소를 제거하는 코드입니다. 위에서 말했듯이, 싱글스레드 코드여도 HashMap을 순회하는 도중에는 삭제하면 안 되기 때문에, Solution1과 같이 ArrayList에 따로 삭제할 항목을 빼내주어 삭제를 하면 됩니다.

Wrong Code

for(Map.Entry<String, Integer> elem : ipTables.entrySet()) {

    String ip = elem.getKey();
    Integer count = elem.getValue();

    ipTables.put(ip, count+1);
    if(count + 1 >= MAX_UPDATE_SIZE) {
        ipTables.remove(ip);
    }
}

Solution 1

ArrayList<String> removeIpList = new ArrayList<>();
for(Map.Entry<String, Integer> elem : ipTables.entrySet()) {

    String ip = elem.getKey();
    Integer count = elem.getValue();

    ipTables.put(ip, count+1);
    if(count + 1 >= MAX_UPDATE_SIZE) {
        removeIpList.add(ip);
    }
}
for(String ip : removeIpList) {
    ipTables.remove(ip);
}

또는, Solution2와 같이 Iterator를 사용할 수도 있습니다.
Solution 2와 같이 iterator를 이용하면, Collection에 발생하는 변경(modification)들을 확인 또는 추적한다고 합니다.(When you create an iterator, it starts to count the modifications that were applied on the collection.)

Solution2

Iterator<String> ips = ipTables.keySet().iterator();
while( ips.hasNext() ) {
  String ip = ips.next();
  Integer count = ipTables.get(ip);

  ipTables.put(ip, count+1);
  if(count + 1 >= MAX_UPDATE_SIZE) {
    ips.remove();
  }
}

 

Reference(참조)