자바에서 멀티스레드 환경에서 Map을 안전하게 사용하려면 반드시 동기화 처리를 고려해야 합니다. 대표적인 방법으로 ConcurrentHashMap과 Collections.synchronizedMap()을 활용한 동기화 맵이 있으며, 두 방식은 내부 동작 원리와 성능 면에서 큰 차이를 보입니다.
ConcurrentHashMap이란?
ConcurrentHashMap은 JDK 1.5부터 도입된 클래스로, 맵에 데이터를 추가하거나 수정할 때 버킷 단위(세그먼트)로만 락을 적용합니다. 따라서 여러 스레드가 동시에 읽기 및 쓰기 작업을 수행할 수 있어 높은 동시성을 제공합니다.
Synchronized HashMap이란?
동기화된 해시 맵(Synchronized HashMap)은 컬렉션 프레임워크의 Collections.synchronizedMap() 메서드를 통해 생성됩니다. 이 방식은 컬렉션 전체에 하나의 락을 걸기 때문에, 한 스레드가 맵에 접근하는 동안 다른 스레드는 해당 맵에 접근할 수 없습니다.
ConcurrentHashMap vs Synchronized HashMap 비교표
| 번호 | 구분 | ConcurrentHashMap | Synchronized HashMap |
|---|---|---|---|
| 1 | 구현 방식 | ConcurrentMap과 Serializable 인터페이스를 구현한 독립적인 클래스입니다. | Collections 클래스에 포함된 메서드입니다. |
| 2 | 락(Lock) 메커니즘 | 맵의 일부분(버킷/세그먼트)만 잠급니다. | 맵 전체를 잠급니다. |
| 3 | 성능 | 동시 읽기·쓰기가 가능하여 동기화 맵보다 성능이 상대적으로 우수합니다. | 여러 스레드가 동시에 접근할 수 없어 성능이 상대적으로 떨어집니다. |
| 4 | Null 키 | null을 키나 값으로 사용할 수 없습니다. | null 키를 허용합니다. |
| 5 | ConcurrentModificationException | 동시 수정 예외가 발생하지 않습니다. | 동기화 맵이 반환하는 이터레이터는 동시 수정 예외를 던질 수 있습니다. |
SynchronizedMap 사용 예제
public class SynchronizedMapExample {
public static void main(String[] args) {
Map<Integer,String> laptopmap = new HashMap<Integer,String>();
laptopmap.put(1,"IBM");
laptopmap.put(2,"Dell");
laptopmap.put(3,"HCL");
// 동기화된 맵 생성
Map<Integer,String> syncmap = Collections.synchronizedMap(laptopmap);
System.out.println("Synchronized map is : "+syncmap);
}
}ConcurrentHashMap 사용 예제
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// ConcurrentHashMap 생성
Map<Integer,String> laptopmap = new ConcurrentHashMap<Integer,String>();
laptopmap.put(1,"IBM");
laptopmap.put(2,"Dell");
laptopmap.put(3,"HCL");
System.out.println("ConcurrentHashMap is: "+laptopmap);
}
}정리
멀티스레드 환경에서 읽기 작업이 많고 높은 동시성이 필요하다면 ConcurrentHashMap이 가장 좋은 선택입니다. 반면 간단한 동기화 처리만 필요하고 성능 요구 사항이 낮다면 Collections.synchronizedMap()으로 충분합니다. 단, null 키 사용 여부와 이터레이션 중 예외 발생 가능성도 함께 고려하여 상황에 맞는 자료구조를 선택하는 것이 중요합니다.