JAVA/Spring

HashMap과 ConcurrentHashMap의 차이점 그리고 동시성 문제 해결

min민 2024. 12. 13.

개발을 하다 보면 데이터를 저장하고 관리하기 위해 HashMap을 사용하는 경우가 많다.

하지만 여러 스레드가 동시에 접근하는 환경에서는 HashMap 사용이 적합하지 않다. 이런 환경에서 동시성 문제를 해결하기 위해 ConcurrentHashMap이 등장한다. 이 글에서는 HashMap과 ConcurrentHashMap의 차이를 이해하고, 언제 어떤 것을 사용해야 하는지 알아본다.

 

1. HashMap의 특징과 문제점

HashMap은 java.util 패키지에서 제공하는 가장 기본적인 Map 구현체이다. 빠른 데이터 삽입과 검색 속도를 제공하며, 단일 스레드 환경에서 매우 유용하다. 하지만 다중 스레드 환경에서는 문제가 발생할 수 있다.

예제: 단일 스레드 환경에서 HashMap 사용

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        map.put("1", "one");
        map.put("2", "two");

        System.out.println(map.get("1")); // 출력: one
    }
}

 

 

위 코드는 단일 스레드 환경에서 잘 동작한다. 그러나 다중 스레드 환경으로 바뀌면 문제가 달라진다.

문제 상황: 다중 스레드 환경에서 HashMap 사용

import java.util.HashMap;
import java.util.Map;

public class MultiThreadedHashMap {
    private static Map<Integer, String> map = new HashMap<>();

    public static void main(String[] args) {
        Runnable writer = () -> {
            for (int i = 0; i < 1000; i++) {
                map.put(i, "Value" + i);
            }
        };

        Runnable reader = () -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(map.get(i));
            }
        };

        Thread thread1 = new Thread(writer);
        Thread thread2 = new Thread(reader);

        thread1.start();
        thread2.start();
    }
}

 

위 코드는 두 개의 스레드가 동시에 HashMap에 접근한다.

  • 첫 번째 스레드는 데이터를 쓰고(put)
  • 두 번째 스레드는 데이터를 읽는다(get).

결과적으로, HashMap에서 동기화가 되어 있지 않기 때문에 데이터가 유실되거나, NullPointerException이 발생할 가능성이 있다.

 

2. ConcurrentHashMap의 등장

ConcurrentHashMap은 HashMap의 동시성 문제를 해결하기 위해 등장한 Map 구현체이다. 내부적으로 락(Lock)을 사용해 데이터를 안전하게 보호하며, 성능 저하를 최소화하는 구조로 설계되었다.

ConcurrentHashMap의 주요 특징

  1. 스레드 안전성
    여러 스레드가 동시에 접근해도 데이터 무결성을 유지한다.
  2. 세그먼트 락
    전체 Map에 락을 거는 것이 아니라, 데이터를 나누어 부분적으로 락을 걸어 성능 저하를 줄인다.
  3. 읽기-쓰기 분리
    읽기 작업은 락을 사용하지 않아 빠르다.

 

 

 

3. HashMap과 ConcurrentHashMap의 비교

특징 HashMap ConcurrentHashMap
동기화 여부 동기화되지 않음 동기화됨
성능 단일 스레드에서 빠름 동시성 문제 해결로 약간의 성능 저하
락(Lock) 사용 여부 없음 있음 (부분 락 사용)
사용 환경 단일 스레드 환경 다중 스레드 환

 

 

4. ConcurrentHashMap 사용 예제

다중 스레드 환경에서 ConcurrentHashMap을 사용하는 예제를 살펴보자.

import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class ConcurrentHashMapExample {
    private static Map<Integer, String> map = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        Runnable writer = () -> {
            for (int i = 0; i < 1000; i++) {
                map.put(i, "Value" + i);
            }
        };

        Runnable reader = () -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(map.get(i));
            }
        };

        Thread thread1 = new Thread(writer);
        Thread thread2 = new Thread(reader);

        thread1.start();
        thread2.start();
    }
}

 

위 코드에서 ConcurrentHashMap은 락을 사용하여 데이터를 보호한다. 두 스레드가 동시에 접근해도 데이터 유실이나 예외가 발생하지 않는다.

 

5. 언제 무엇을 사용할까?

  • 단일 스레드 환경
    HashMap을 사용하는 것이 적합하다. 성능이 더 빠르기 때문이다.
  • 다중 스레드 환경
    ConcurrentHashMap을 사용해야 한다. 데이터의 안전성을 보장할 수 있기 때문이다.

 

결론

동시성 문제를 해결하기 위해 ConcurrentHashMap은 필수적이다.
단순히 성능만 고려해 HashMap을 사용하는 것은 위험할 수 있다.
개발자는 항상 프로그램의 실행 환경데이터의 안전성을 고려해 적합한 자료구조를 선택해야 한다.

결국, "스레드 안전성"이 필요한 상황이라면 ConcurrentHashMap을, 그렇지 않다면 HashMap을 사용하면 된다.
스마트한 개발자는 항상 환경에 맞는 올바른 도구를 선택해야 한다.

 

댓글