개요
ConcurrentHashMap은 JDK 1.5에서 도입된 동시성(concurrent) 컬렉션 클래스입니다. 맵에 데이터를 추가하거나 수정할 때 버킷(bucket) 수준, 즉 세그먼트(fragment) 단위로만 락을 적용하기 때문에, 읽기와 쓰기 작업이 서로 다른 스레드에서 동시에 수행될 수 있습니다.
반면 HashTable은 JDK 1.1에서 도입된 스레드 안전(thread-safe) 레거시 클래스로, Map 인터페이스의 초기 구현체입니다. null 키와 null 값을 허용하지 않으며, 내부적으로 동기화(synchronized)되어 있어 두 개 이상의 스레드가 동시에 접근할 수 없습니다. 또한 저장된 요소들의 순서는 유지되지 않습니다.
HashTable vs ConcurrentHashMap 비교표
| 번호 | 구분 기준 | HashTable | ConcurrentHashMap |
|---|---|---|---|
| 1 | 기본 개념 | JDK 1.1에서 도입된 스레드 안전 레거시 클래스 | JDK 1.5에서 도입된 고성능 동시성 컬렉션 클래스 |
| 2 | 락(Locking) 방식 | 컬렉션 전체에 락을 적용 | 데이터 추가·수정 시 버킷(세그먼트) 수준에만 부분적으로 락을 적용 |
| 3 | 성능 | ConcurrentHashMap보다 느림 | 세밀한 락 제어 덕분에 HashTable보다 우수한 성능 |
| 4 | Null 처리 | null 키와 null 값을 허용하지 않음 | null 키와 null 값을 허용하지 않음 (허용 시 NullPointerException 발생) |
Hashtable 사용 예제
import java.util.Hashtable;
import java.util.Map;
public class HashtableExample {
public static void main(String[] args) {
// Hashtable 생성
Hashtable<String, String> map = new Hashtable<>();
map.put("HCL", "100");
map.put("DELL", "200");
map.put("IBM", "300");
// 맵 출력
for (Map.Entry<String, String> m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
}ConcurrentHashMap 사용 예제
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// ConcurrentHashMap 생성
Map<String, String> myMap = new ConcurrentHashMap<>();
myMap.put("HCL", "1");
myMap.put("DELL", "2");
// 맵 출력
for (Map.Entry<String, String> m : myMap.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
}정리: 어떤 것을 사용해야 할까?
멀티스레드 환경에서 Map을 공유해야 한다면 ConcurrentHashMap이 가장 좋은 선택입니다. 버킷 단위의 세밀한 락 제어 덕분에 HashTable보다 동시성 성능이 크게 뛰어나며, 현재 자바 표준 API에서도 권장되는 방식입니다. 반면 HashTable은 하위 호환성을 위해 유지되고 있을 뿐, 신규 프로젝트에서는 사용하지 않는 것이 좋습니다. 단일 스레드 환경이라면 동기화 오버헤드가 없는 HashMap을 사용하는 것이 효율적입니다.