먼저 각각에 대해 설명해보자.
1. Collections.synchronizedMap
기존 Map 객체를 감싸는 팩토리 메소드이다. Collections 의 내부 클래스 중 하나인 SynchronizedMap 을 반환한다. 이 클래스는 mutex 라는 이름으로 선언된 객체의 락을 사용하여 모든 연산을 동기화 해준다. 여러 스레드에서 접근해도 연산이 정상 동작함을 보장한다. 기존 Map 객체는 동기화를 지원하지 않으므로, 이 메소드로 한번 감싸면 동기화 컬렉션이 된다.
2. ConcurrentHashMap
java.util.concurrent 패키지에 포함된 병렬 컬렉션 중 하나이다. Map 인터페이스의 구현체이며, Collections.synchronizedMap 와 마찬가지로 여러 스레드에서 접근해도 연산이 정상 동작함을 보장한다.
두 컬렉션은 모두 여러 스레드가 접근할 때 연산이 정상 동작함을 보장하는 동기화 컬렉션이다. 그런데 둘을 구분한 이유는 무엇일까?
다른 방식의 동기화 기법이 적용되어 있기 때문이다. 전자는 어떤 연산이든 특정 락을 잡은 스레드만 사용 가능하고 나머지 스레드는 기다려야 한다. 하지만 후자는 락 스트라이핑이라는 기법을 채택하여 여러 스레드가 한번에 접근해도 정상 동작한다. ConcurrentHashMap은 디폴트 16개의 세그먼트라는 단위로 분리하여 각 세그먼트에 락을 하나씩 배정한다. 참고로 각 버킷의 hashcode 의 16으로 나눈 나머지 값으로 배정한다.
(n - 1) & hash == (tab.length - 1) & hash == (16 - 1) & hash == hash % 16
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // no lock when adding to empty bin
}
...
}
'Java' 카테고리의 다른 글
java의 HashMap 해시 충돌 해결 (1) | 2022.01.03 |
---|---|
Object 객체의 equals와 hashCode (0) | 2022.01.03 |