Synchronized Collection과 Concurrent Collection의 차이에 대해 정리해보려한다.
Synchronized Collection에는 대표적으로 다음과 같은 클래스가 있다.
해당 클래스들은 모두 메서드에 synchronized를 사용해 한번에 하나의 스레드만 사용할 수 있도록 하면서 동시성을 보장해주고 있다.
대표적으로 Vector를 살펴보면, 거의 대부분의 메서드에 synchonized가 붙어있는 걸 확인할 수 있다.
다음으로 Collections.synchronizedList를 살펴보면 마찬가지로 모든 메서드에 synchronized가 붙어있고, 추가된 점으로는 mutex를 사용한 것을 볼 수 있다.
이는 동시성을 제어하기 위한 공통적인 객체로 하나의 스레드가 synchronized 블록안에 들어가면 다른 메서드의 synchronized 부분이 모두 lock이 걸리게 된다.
이렇게 다른 스레드의 동작을 blocking 해버리기 때문에, 성능 상 상대적으로 ConcurrentCollection에 비해 열세일 수 밖에 없다.
java.util.concurrent 패키지 아래에서 아래 클래스 외에도 많은 Concurrent Collection을 제공한다.
이 중 ConcurrentHashMap의 putVal 메서드를 살펴보자.
(코드가 길어 이하는 생략)
해시 버킷이 비어있는 경우와 그렇지 않은 경우로 나누어서 설명하려 한다.
volatile과 CAS 알고리즘에 대해 간단히 정의하면 아래와 같다.
vaolatile : 변수를 CPU cache가 아닌 Main Memory에 저장하게 해주는 예약어. 변수의 값을 read/write 시 Main Memory에서 처리한다.
이렇게 하면, Multi Thread 환경에서 해당 값을 read해올때 가장 최신 값을 보장해줄 수 있으나, Multi Thread가 동시에 Write하는 경우에는 최신 값을 보장할 수 없어 synchronized를 사용해야한다.
CAS 알고리즘 : 현재 스레드가 가지고 있는 기존 값과 메모리가 가지고 있는 값을 비교해 같은 경우 변경할 값을 메모리에 반영하고 true를 반환, 다른 경우에는 변경 값이 반영되지 않고 false 반환 후 재시도 하는 알고리즘
Concurrent Collection이 내부적으로 어떻게 syncronized를 적게 사용하는지에 대해 내용 설명이 부족한 것 같아 추가.
위에서 다룬 ConcurrentHashMap을 기준으로 설명하자면,
우선 해당 컬렉션(다른 Concurrent들도 포함)은 제한적으로 여러 스레드가 동시에 접근 가능하다.
1. JAVA 8 이전