Java

[Java 기초] HashMap 대신 왜 ConcurrentHashMap을 써야 할까?

뭉뭉규 2025. 7. 7. 18:26

`HashMap`은 자바에서 가장 많이 사용되는 자료구조 중 하나입니다.  
하지만 단순히 데이터를 저장한다고 해서 항상 `HashMap`을 써도 되는 것은 아닙니다.

만약 여러 스레드가 동시에 접근할 가능성이 있는 상황이라면,  
`HashMap` 대신 반드시 `ConcurrentHashMap`을 사용해야 합니다.

이번 글에서는 ConcurrentHashMap이 필요한 이유, 그리고 HashMap과의 차이점을 정리해보겠습니다.


HashMap은 멀티스레드 환경에서 위험하다

먼저 HashMap은 기본적으로 스레드 안전하지 않은 컬렉션입니다.

즉, 여러 스레드가 동시에 put(), get(), remove() 같은 작업을 하면 내부 데이터 구조가 꼬일 수 있습니다.

예를 들어, 다음과 같은 코드가 있다고 가정해봅시다:

Map<String, Integer> countMap = new HashMap<>();

// 스레드 A
countMap.put("user1", 1);

// 스레드 B
countMap.put("user1", 2);

이런 동시 접근 상황에서 HashMap은 비정상 동작을 하거나, 심지어 무한 루프에 빠져 애플리케이션이 멈추는 경우도 발생할 수 있습니다.


ConcurrentHashMap은 동시성 문제를 해결해준다

반면, ConcurrentHashMap은 이름 그대로 동시 접근(Concurrency) 상황에서도 안전하게 동작하도록 설계된 Map입니다.

Collections.synchronizedMap()과 같은 방법도 존재하지만,
이 방식은 Map 전체에 synchronized 키워드를 걸기 때문에 항상 한 번에 하나의 스레드만 접근할 수 있습니다.
즉, 병렬 처리가 불가능하고 성능 저하가 발생할 수 있습니다.

 

반면 ConcurrentHashMap은 내부적으로 키를 기준으로 데이터를 분산 저장하고,
필요한 영역에만 잠금을 걸기 때문에 병목이 적고 성능이 뛰어납니다.

 

Java 8 이전에는 내부적으로 Segment라는 구조를 사용하여 분할 락(Lock Striping)을 구현했지만,
Java 8부터는 세그먼트 없이도 CAS(Compare-And-Swap)와 synchronized 블록을 조합 (각 테이블 버킷을 독립적으로 잠그는 방식)하여
더 정교하게 병렬 처리를 수행합니다.

Map<String, Integer> countMap = new ConcurrentHashMap<>();

// 스레드 A
countMap.put("user1", 1);

// 스레드 B
countMap.put("user1", 2);

// 스레드 C
Integer value = countMap.get("user1");

이렇게 여러 스레드가 동시에 접근해도 문제 없이 안전하게 작동합니다.


언제 ConcurrentHashMap을 써야 할까?

  • 전역 저장소에 데이터를 저장할 때
  • 웹 서버에서 요청 처리 시, 여러 사용자의 세션을 Map에 저장할 때
  • 캐시(Cache)나 공유 상태를 저장하는 경우

여러 사용자의 데이터를 저장하거나 관리하는 공용 저장소를 직접 구현할 때는,
동시에 여러 스레드가 접근할 수 있으므로 HashMap 대신 ConcurrentHashMap을 사용해야 합니다.

private static Map<Long, Member> memberStore = new ConcurrentHashMap<>();

 

여러 스레드에서 동시에 이 저장소에 접근할 수 있고,
싱글톤처럼 한 저장소를 공유할 의도라면 반드시 static이어야 합니다.


동시에 접근할 수 있는 상황 이라면 ConcurrentHashMap을 

단순히 데이터를 저장한다고 무조건 HashMap을 써서는 안 됩니다.
동시에 접근할 수 있는 상황이라면 (특히, 실무에서는)  ConcurrentHashMap을 써야 합니다.

단일 스레드 → HashMap
멀티 스레드 → ConcurrentHashMap