본문 바로가기

Programming/Java

[Java] Hashtable VS HashMap VS ConcurrentHashMap

Hashtable VS HashMap VS ConcurrentHashMap


Hashtable, HashMap, ConcurrentHashMap 모두 Map 인터페이스를 implements한 AbstractMap을 상속받아 구현하고 있는 공통점은 있다.

그럼 어느 부분이 다를지 알아보자.

 

Hashtable VS HashMap

Hashtable은 모든 데이터 변경 메소드가 synchronized로 선언되어 있어서, 메소드 호출 전 스레드 간의 동기화 lock을 통해 멀티 스레드 환경에서 data의 무결성을 보장해준다. (thread-safe)

하지만 HashMap은 그런 장치가 1도 없기 때문에, 동기화 문제가 발생할 수 있다(non-thread-safe).
물론, HashMap도 synchronized로 잘 잡아주면 동기화 문제가 해결될 수도 있긴 하다.

동기화 lock이 엄청 느린 동작이기 때문에 성능은 HashMap이 훨씬 우수하다.
그 외에 put()으로 삽입하고 get()으로 조회하는 것 등등의 사용법은 둘이 일치한다.

 

ConcurrentHashMap

자바 컬렉션에서도 thread-safe한 map이 있는데, 이게 ConcurrentHashMap이다.

HashMap과 다르게 key, value에 null값을 허용하지 않고, putIfAbsent()라는 메소드를 갖고 있다.

동기화를 지원한다는 것이 Hashtable과 같지만 성능은 ConcurrentHashMap이 더 우수하므로 그냥 이걸 사용하는 쪽을 추천한다.

 

Map 객체의 동기화가 필요한 상황에서

동기화가 필요하면 HashMap을 프로그래밍 관점의 편리함을 위해 아래와 같이 한번 감싸서 사용하거나 ConcurrentHashMap을 사용하면 된다.
(어떤 게 성능이 더 좋은지는 아직 확인하지 못했다.. 바쁜게 끝나면 모든 성능비교를 직접 해보고 업데이트 할 것이다.)

Map m = Collections.synchronizedMap(new HashMap(...));

 

정리


  Hashtable HashMap ConcurrentHashMap
thread-safe
(synchronized)
O X O
반환값 Enumeration Fail-Fast Iterator Fail-Safe Iterator
성능 순위 3 1 2
key, value에 null 허용 X O X
사용할 상황 X 단일 스레드에서만 map을 사용하는 경우 멀티 스레드 환경에서 map을 사용하는 경우

 

 

HashMap과 ConcurrentHashMap의 성능 비교


내가 직접 한 게 아니라 어떤 환경에서 했는지 100% 이해하지 못했지만, 어떤 분이 했던 성능 비교에 의하면 대략 이런 결과가 나왔다고 한다.

아래 두 가지 상황에서 비교했다.

  1. 싱글 스레드에서의 HashMap과 ConcurrentHashMap
  2. 코어 개수만큼의 멀티 스레드 환경에서 ConcurrentHashMap은 모든 스레드가 같은 Map을 공유하고 (SharedConcurrentHashMap 항목),
    HashMap은 스레드마다 별도로 갖도록 했다. (DifferentHashMap 항목, HashMap을 스레드별로 파티션하면 성능이 나아지는지 알아보기 위해 이렇게 한 듯 하다.)

그리고 그래프의 축은

  • Y축 : 시간
  • X축 : map의 size

이므로 완만한 모양을 띌수록 성능이 좋다.

 

add 비교
get 비교
remove 비교

결론

  1. 멀티 스레드 환경이 더 성능이 좋다. size가 커질수록 적극적으로 멀티 스레드를 도입해야한다.
  2. 하지만 굳이 HashMap을 partition해서 각 스레드마다 갖도록 할 필요는 없어 보인다.
    ConcurrentHashMap을 share했을 때와 성능 차이가 딱히 없는 수준이기 때문이다.
    (노란색 선과 초록색 선은 기울기가 비슷함에서 알 수 있다.)
  3. 싱글 스레드라면 당연히 HashMap을 쓰는게 빠르다. 특히 add에서는 3배 가량 빠르다.
    get에서는 물론 ConcurrentHashMap이 좀 더 빠르지만 아주 큰 차이는 아니다.

놀랐던 것은 ConcurrentHashMap의 단일 스레드 성능도 꽤나(?) 나쁘지 않다는 점이었으나, 아무튼 어떤 상황에서 add, remove, get의 '성능'을 위해서는 어떤 결정을 내려야할지 대략적인 근거를 삼을 수 있게 되었다.

 

중요한 것은 thread-safe하든 하지 않든 그 내용을 잘 문서화 해두며 코딩하는 것이기도 하다.
(추후 수정하는 사람을 위해..)

 

 

References