Collections.synchornizedMap(HashMap)
을 사용해서 HashMap에 래핑 작업(?)을 거쳐 thread-safe하게 만들 수 있다.Enumeration
을 HashMap은 Fail-Fast iteration
을 사용한다.Enumeration과 Iterator
모두 컬렉션 요소를 순회할 수 있는 기능을 제공한다는 공통점이 있다.remove()
메소드를 제공하지만 Enumeration은 그렇지 않다. Enumeration은 요소들을 탐색할 수만 있기 때문에 읽기 전용
인 경우에만 사용할 수 있다.ConcurrentModificationException
이 발생하지만 Enumeration은 그렇지 않다.Concurrent Modification
Concurrent Modification
은 다른 작업이 수행하고 있는 객체를 동시에 수정하는 것이다.Fail-Fast Iterator
Fail-Fast
는 가능한 빨리 예외가 노출시키고 동작을 중단시킨다.
컬렉션은 요소가 추가되거나 삭제연산이 수행될 때 마다 증가하는 modCount
라는 변수를 가지고 있다.
Fail-Fast Iterator가 생성될 때 해당 컬렉션에 대한 modCount
를 Iterator의 내부 변수에 복사한다.
Iterator의 next() 메소드를 호출할 때 복사된 modCount와 원본 modCount가 다르면 ConcurrentModificationException
을 발생시킨다.
Map<String, String> map = new HashMap<>();
Map.put("foo1", "bar1");
Map.put("foo2", "bar2");
//iterator를 생성하는 시점에 원본 modCount를 iterator 내부로 복사
Iterator iter = map.keySet().Iterator();
while(iter.hasNext()){
System.out.println(iter.next()); //원본 modCount와 iterator 내부 modCount를 비교
map.put("foo3", "bar3"); // 요소를 추가하면 다음 next() 메소드 호출시 예외를 발생시킴
}
ArrayList, HashMap, Vector
등이 Fail-Fast Iterator를 사용한다.Fail-Safe Iterator (non-Fail fast)
Fail-Safe
는 예외 발생시 작업을 중단하지 않고 가능한한 예외를 회피하는데 초점을 둔다.
Fail-Safe Iterator는 원본 컬렉션의 복사본에 대해서 순회 연산을 하기때문에 순회 도중에 컬렉션이 수정되더라도 어떠한 예외도 발생시키지 않는다.
CopyOnWriteArrayList<Integer> list
= new CopyOnWriteArrayList<Integer>(new Integer[] {1, 7, 9, 11});
// 원본 컬렉션에 대해 복사본을 생성하고 그에 대한 iterator를 반환한다.
Iterator<Integer> itr = list.Iterator();
while (itr.hasNext()) {
Integer i = itr.next();
System.out.println(i);
if (i == 7) {
list.add(15); // 원본 컬렉션에 추가한 요소는 출력도 되지않고 예외도 발생하지 않는다.
}
}
ConcurrentHashMap과 CopyOnWriteArrayList
등이 Fast-Safe를 사용한다.ConcurrentHashMap은 Fail-Safe이지만, 원본 컬렉션을 사용한다.
ConcurrentHashMap
의 경우, Fail-Fast는 아니지만 복사된 컬렉션을 사용하지는 않는다.ConcurrentHashMap<String, Integer> m = new ConcurrentHashMap<String, Integer>();
m.put("ONE", 1);
m.put("SEVEN", 7);
m.put("FIVE", 5);
m.put("EIGHT", 8);
Iterator<String> it = m.keySet().Iterator();
while (it.hasNext()) {
String key = it.next();
System.out.println(key + " : " + m.get(key));
m.put("NINE", 9);
}
[출력 결과]
EIGHT : 8
FIVE : 5
NINE : 9 // 순회할 때 추가한 원소가 출력됐다.
ONE : 1
SEVEN : 7
m.put("NINE", 9)
을 통해 요소를 추가했는데, 그 요소가 그대로 출력된 것을 볼 수 있다.ConcurrentHashMap
은 원본 컬렉션에 대해서 순회 연산을 한다고 볼 수 있다.ConcurrentHashMap
은 HashTable
의 단점을 보완하는 자료구조이다.HashTable vs ConcurrentHashMap1
HashTable vs ConcurrentHashMap2
Fail-Safe Iterator vs Fail-Fast Iterator