AtomicInteger와 같이 AtomicReference를 사용하면 ConcurrentModificationException(이하 CME)를 만날 수 있다.
AtomicReference(Collection)으로 사용하고 있을 때 내부적으로 AtomicReference 내부의 volatile으로 저장된 Collection value은 set/get에 대해서는 atomic 한다. 하지만 volatile Collection value의 iterator는 atomic하지 않을 수 있다. 따라서 AtomicReference로 감싸고 있는 Collection을 사용할 때는 주의하면서 써야할 필요가 있다.
Java 7 이전에는 Collections.sort() 메소드 호출시 문제가 있을 수 있었지만, 체크하는 로직이 없었다. CME가 발생되지 않았다.
Collections를 새롭게 구현한 Java8의 Collections.sort() 내에서 소팅하기 전에 count(정확히 AbstractList#modCount, https://docs.oracle.com/javase/8/docs/api/java/util/AbstractList.html#modCount)를 저장하고 소팅하고 나서 저장된 count가 기존에 저장된 것과 비교해서 다르면 CME를 발생하도록 되어 있다. 좀 더 고급스럽게 말하면 CME를 빨리 발생시켜주는 fast-fail iterator를 위한 것이다.
아래 코드는 Java 7를 포함, 그 이하버전에서는 CME가 발생하지 않았다.
private AtomicReference<List<String>> myList = new AtomicReference<List<String>>();
public List<String> sort() {
List<String> result = new ArrayList<String>();
List<String> list = this.myList.get();
if (list == null) {
return null;
}
for (String string : list) {
result.add(string);
}
Collections.sort(list);
return result;
}
그러나 Java8에서 Collections.sort() 에서 위에서 설명한 modCount 덕분에 빨리 CME가 발생하게 되었다.
관련해서는 http://stackoverflow.com/questions/10658478/concurrent-modification-exceptions에서 비슷한 내용이 있다.
재미있는 것은 안전한 iterator를 구현한 ConcurrentMap 이다. weak consistent한 iterator를 보장한다.
그리고, set/get/iterator의 안전함이 ConcurrentMap의 thread-safe를 구현하고 있다. 관련 내용은 아래 링크에 자세히 설명되어 있다.
'java core' 카테고리의 다른 글
[java-linux] thread의 nid 값(16진수)를 얻어 10진수의 nid의 값으로 변환 (0) | 2014.12.02 |
---|---|
oracle jdk 1.7 java source (sun 패키징 포함) (0) | 2014.11.28 |
[java 8]-XX:MaxTenuringThreshold 설정값 변경 (0) | 2014.11.13 |
WeakHashMap을 thread-safe하게 하기 (WeakHashMap의 무한루프(infinite loop) 해결하기) (0) | 2014.11.05 |
List.subList(fromIndex, toIndex) 실수 (0) | 2014.10.31 |