최근 자바병렬프로그래밍 책으로 스터디를 하고 있는데, 아래 문구를 이해하기가 쉽지 않았다.
ConcurrentHashMap
클래스는 독점적으로 사용할 수 있는 락이 없기 때문에,Vector
클래스를 대상으로 살펴봤던 것처럼 클라이언트 측 락 기법을 활용할 수 없다.
활용할 수 없다... 왜? 왜 안되는 걸까? 궁금증을 해소하기 이것저것 알아보기 시작했다. stackoverflow에 비슷한 질문이 있는 거 보면 나만의 궁금증은 아닌 것 같다.
우선 Vector
클래스를 알 필요가 있다. Vector
와 ArrayList
를 비교하곤 하는데 주요 차이점은 잘 알다시피 동기화 처리 유무이다. 그래서 Vector
는 멀티 스레드에서 안전하고 ArrayList
는 그렇지 않다. 하지만 synchronized 처리로 인해 Vector
는 ArrayList
보다 느리기 때문에 보통은 ArrayList
를 사용하고 Vector
는 많이 사용되지 않는다.
Vector
클래스를 살펴보면 주요 메서드에 synchronized 키워드가 있는 것을 볼 수 있다. synchronized 키워드가 메서드에 있다면, 메서드가 있는 클래스 객체를 기준으로 락이 지정된다. 다시 말하면 같은 Vector
객체에서 synchronized가 지정된 메서드가 실행되고 있다면 같은 객체 다른 synchronized 메서드 실행은 대기를 하게된다. 그렇기 때문에 아래와 같이 클라이언트 락을 지정하더라도 문제가 발생하지 않는다. (Vector
의 add 메서드에는 synchronized가 지정되어 있다.)Vector<Integer> vector = new Vector<>();
public void addSync(Integer element) {
synchronized (vector) {
vector.add(element);
}
}
public void add(Integer element) {
vector.add(element);
}
하지만 ConcurrentHashMap
은 Vector
처럼 메서드에 synchronized가 지정된 방식이 아니라 내부적으로 여러 개의 세그먼트로 두고 각각 별도 락을 지정하는 방식으로 처리를 하는데, 이런 방법을 lock striping이라고 한다. 이는 메서드에 지정한 방식보다 동시성이 더 좋다. 이런 차이점으로 인해 ConcurrentHashMap
은 아래와 같이 클라이언트 락을 지정하더라도 동시성을 보장받을 수 없다.
private Map<Integer, Integer> hashMap = new ConcurrentHashMap<>();
public void putSync(Integer key) {
synchronized (hashMap) {
hashMap.put(key, Integer.MIN_VALUE);
}
}
public void put(Integer key) {
hashMap.put(key, Integer.MIN_VALUE);
}
알고 보면 당연하고 간단한 이유인데 한참을 고민하고 나서야 이해를 할 수 있었다. Vector
는 메서드에 synchronized 키워드가 지정되어 있고, 메서드에 synchronized가 지정되면 어떻게 동작하는지 알고 있었다면 쉽게 이해했을 것이다.
'Dev > Java' 카테고리의 다른 글
자바 메모리 누수 확인 (0) | 2017.04.14 |
---|---|
멀티 스레드에서 synchronized가 필요한 경우 (0) | 2017.04.13 |
CompletableFuture에 관해서 (0) | 2017.02.27 |
Collector 인터페이스 (0) | 2017.02.14 |