•
Hashmap은 thread-safe하지 않다
•
동시성을 보장받고 싶다면 synchronized 키워드를 사용하여 객체 전체에 락을 걸거나
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
public synchronized int size() { }
@SuppressWarnings("unchecked")
public synchronized V get(Object key) { }
public synchronized V put(K key, V value) { }
}
Java
복사
•
java.util.Collections의 synchronizedMap 또는 java.util.concurrent의 ConcurrentHashMap을 사용해야 한다
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("A", 1);
int value = map.get("A");
map.remove("A");
System.out.println("Map size: " + map.size());
}
}
Java
복사
•
ConcurrentHashMap은 map의 일부에만 lock을 건다 (읽기 할때는 X, 쓰기만)
•
자바 8 이전에는 내부적으로 Segment(구역)라는 개념을 만들고, 각 Segment 내부에서는 ReentrantLock을 사용하여 영역 별로 잠금을 걸 수 있도록 했다. 이렇게 하면 여러 스레드에서 동시에 데이터를 수정하더라도, 서로 다른 Segment 내의 데이터를 수정할 수 있다.
•
자바 8 이후부터는 각 테이블 버킷을 독립적으로 잠그는 방식을 사용한다.
◦
빈 버킷에 노드를 삽입할 경우에는 락 대신 CAS 알고리즘 을 사용한다.
▪
CAS: 원자성을 보장하기 위해 기대되는 값과 비교하여 일치 하는 경우에만 값을 수정
▪
기대값(null)을 비교하여(casTabAt) 같으면(null이면) 새로운 Node를 새로운 노드를 넣고 같지않다면 다시 반복문으로 돌아간다.
◦
해시 버킷에 이미 노드가 존재하는 경우에는 해당 버킷의 첫번째 node에 synchronized block을 사용하여 락을 걸고 하나의 스레드만 접근하도록 제어한다.
참고 자료: