개발일지/Spring

동시성 문제를 고려한 HashMap<>() 선택하기

꾸주니=^= 2025. 2. 7. 09:43

 

Spring에서 HashMap을 저장소로 사용할 때 동시성 문제를 고려하는 것은 중요합니다. 


HashMap은 Java의 key-value(키-값) 쌍을 저장하는 자료구조로, java.util 패키지에 포함된 해시 테이블 기반의 맵이고, 동기화되지 않은 자료구조입니다.

public class MemoryMemberRepository implements MemberRepository{
    // DB 확정이 되지 않아서 Memory용 test용으로만 사용

    private static Map<Long, Member> store = new HashMap<>();
    // 동시성 문제를 고려한다면 ConcurrentHashMap<>() 을 사용해야 한다.

 


1. HashMap과 동시성 문제

HashMap은 동기화되지 않은 자료구조이므로, 멀티스레드 환경에서 사용하면 데이터 손실이나 충돌이 발생할 수 있습니다.

예를 들어, 두 개 이상의 스레드가 store.put(key, value)를 동시에 실행할 경우

  • 한 스레드가 데이터를 추가하는 동안 다른 스레드가 데이터를 덮어쓸 가능성이 있음
  • 한 스레드가 데이터를 읽으면서 다른 스레드가 데이터를 수정하면 읽기 도중 데이터 불일치 문제가 발생할 수 있음
  • 결국, 일관성이 깨지고 예기치 않은 버그가 발생할 수 있음

 

2. ConcurrentHashMap vs. Collections.synchronizedMap()

멀티스레드 환경에서 HashMap을 안전하게 사용하려면 아래 두 가지 방법을 고려할 수 있습니다.

(1) Collections.synchronizedMap()

Map<Long, Member> store = Collections.synchronizedMap(new HashMap<>());
  • synchronizedMap()은 맵 전체에 대해 락을 걸어 동기화합니다.
  • 단점: 모든 스레드가 put(), get() 등 어떤 작업을 하든 맵 전체를 락으로 보호하므로 성능이 저하됩니다.
  • 즉, 한 스레드가 데이터를 추가하거나 읽는 동안 다른 스레드는 대기해야 하므로 경합(Contended Lock)이 심해짐.

 

(2) ConcurrentHashMap

private static Map<Long, Member> store = new ConcurrentHashMap<>();
  • ConcurrentHashMap은 내부적으로 여러 개의 세그먼트(구역)를 나눠서 동기화합니다.
  • 즉, 일부 데이터만 접근할 때 해당 영역에만 락을 걸기 때문에 성능이 더 우수합니다.
  • 여러 스레드가 동시에 데이터를 읽고 쓸 때도, 필요한 부분에만 락이 걸리므로 synchronizedMap()보다 병렬 처리가 훨씬 효율적입니다.

결론적으로 멀티스레드 환경에서 안전한 HashMap 대체 방법을 정리하면

  • 단순한 동기화가 필요할 경우 : Collections.synchronizedMap(new HashMap<>()) -> 비효율적
  • 동시성이 중요한 경우 : ConcurrentHashMap<>() -> 효율적

 

따라서 위 코드에서 동시성을 고려한다면 ConcurrentHashMap<>을 사용하는 것이 적절한 선택입니다.