Java에서 HashMap은 단일 스레드에서는 빠르게 작동하지만, 멀티스레드 환경에서는 안전하지 않다.
여러 스레드가 동시에 put()이나 get()을 호출하면 데이터 손상, ConcurrentModificationException, 무한 루프 같은 심각한 문제가 발생할 수 있다.
HashMap의 멀티스레드 한계
Map<String, String> map = new HashMap<>();
- 스레드 간 동기화가 없기 때문에 동시 접근 시 데이터 충돌 가능성이 있다
- 최악의 경우 Iterator 사용 중 ConcurrentModificationException이나 무한 루프도 일어난다.
ConcurrentHashMap – 안전하고 빠른 멀티스레드 Map
- 스레드 안전(Thread-safe): 여러 스레드가 동시에 접근해도 안전
- 높은 병렬성: 내부적으로 버킷(bin) 단위로 동기화 → 성능 최적화
- null 금지: key, value 모두 허용 X (null 입력 시 NPE)
- JDK 1.8부터는 Bin 기반 락, 이전 버전은 Segment 기반
내부 동작 원리
① 초기 설계 (JDK ≤1.7)
- 데이터를 여러 세그먼트로 분할
- 각 세그먼트마다 독립 락 → 세그먼트 단위 병렬성 확보
② 개선된 구조 (JDK ≥1.8)
- Bin(버킷) 단위 락 + CAS, volatile 활용한 비차단 동기화
- 동시성 수준 높은 읽기/쓰기 가능
핵심 API 사용법
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("apple", 1);
map.computeIfAbsent("orange", key -> 3);
map.replace("banana", 2, 20);
map.forEach((k, v) -> System.out.println(k + " : " + v));
- computeIfAbsent, putIfAbsent, replace 등 원자적(operation atomic) 연산 제공
- null key/value 입력 시 즉시 NullPointerException
동작 비교
| HashMap | Hashtable | synchronizedMap | ConcurrentHashMap | |
| Thread-Safety | ❌ | ✅ (모두 락) | ✅ (전체 락) | ✅ (부분 락 고성능) |
| null 허용 | ✅ | ❌ | ❌ | ❌ |
| 성능 | 빠름 | 느림 | 중간 | 빠름 |
| 동시성 수준 | 없음 | 없음 | 전체 락 | 버킷 단위 병렬성 |
ConcurrentHashMap 완전한 해결책은 아니다
- containsKey + put 같은 결합된 연산은 여전히 경쟁 상태(race condition) 취약하다
- 이런 경우 putIfAbsent, computeIfAbsent 등의 atomic 메서드 사용이 필수다
null value 금지
- map.put(null, x) 또는 map.put(k, null) 시 즉시 예외 발생
iterator 사용 시
- forEach 같은 내부 반복 메서드는 안전하지만
- iterator() 사용 중 구조 변경 시 예측 불가한 결과 나올 수 있음 (예: 중복, 누락)
예시
- 캐시 구현: Spring Cache의 기본 구현체에서 ConcurrentHashMap 활용
- 세션 저장소, 통계 집계, 횟수 카운팅 등
- 멀티스레드 서버, 이벤트 처리 루프, 스케줄러 동시 실행 환경 등
- 단일 스레드 환경에서는 HashMap도 충분하지만,
- 멀티스레드 환경에서는 보장된 동시성(Hashtable, synchronizedMap)보다
→ ConcurrentHashMap이 부분 락 + CAS 기반으로 더 효율적이다 - 그러나 putIfAbsent, compute* 같은 atomic 메서드를 사용해야 경쟁 조건 방지 가능하며,
- iterator 사용 중 중복/누락 발생할 수 있음에 주의해야 한다
반응형
'IT > JAVA' 카테고리의 다른 글
| Java 예외: RuntimeException (0) | 2025.12.06 |
|---|---|
| 동기화 vs 원자적 연산 (1) | 2025.07.24 |
| Java String 클래스 (0) | 2025.04.15 |
| JMH (JMH 라이브러리를 활용한 Java 코드 성능테스트) (2) | 2025.04.11 |
| ArrayList vs LinkedList (0) | 2025.04.08 |