• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

ThreadLocal 동시성 이슈 (ArrayList, HashMap, HashSet)

21.11.10 13:38 작성 조회수 1.27k

5

안녕하세요! 김영한님!!
수업 너무 유익하게 잘 듣고있습니다~!! 
동시성 이슈를 막기 위해 ThreadLocal을 사용하는 부분 중에서 궁금한 점이 생겨서 질문 드립니다.

String, TraceId의 타입에 대해서는 ThreadLocal을 사용해서 동시성 이슈를 해결하였는데, 필드로 ArrayList, HashMap, HashSet을 사용하고 싶은 경우에는 동시성 이슈를 어떻게 해결하면 될까요 ? 

구글링을 했었을 때에는 멀티쓰레드 환경에서는 HashMap 대신에 ConcurrentHashMap을 사용해라고 봤었고, ArrayList 대신에 CopyOnWirteArrayList를 사용해라고 봤었고, HashSet에 대해서는 제대로 찾지 못했습니다.

멀티쓰레드 환경에서 Thread-Safe하게 ArrayList, HashMap, HashSet을 사용하려면 어떻게 해야하나요 ?

답변 1

답변을 작성해보세요.

29

안녕하세요. 박재성님

동기화 된 컬렉션을 만드는 방법은 다음을 참고해주세요.

https://cornswrold.tistory.com/209

먼저 말씀드리면 강의 예제에서 설명드린 동시성 문제와 ConcurrentHashMap, 동기화 된 컬렉션등의 동시성 문제는 다른 문제입니다.

 

ConcurrentHashMap이나, 동기화 된 컬렉션은 데이터를 입력할 때와 조회할 때만 동시성이 보장됩니다.

예를 들어서

쓰레드 A: map.put("key", "abcdefg")

쓰레드 B: map.get("key")

이 경우 A의 map.put("key", "abcdefg")가 조금이라도 빠르다면 A가 먼저 실행되고, 그 다음에 B가 실행됩니다.

그런데 잘 생각해보시면, ConcurrentHashMap을 사용하더라도 이번 예제에서 발생하는 동시성 이슈를 막을 수는 없습니다. 왜냐하면 강의 예제에서 발생하는 동시성 문제는 단순히 put, get을 동시에 호출하는 문제가 아닙니다. put, get을 순서대로 호출해도 발생하는 더 넓은 범위의 동시성 문제입니다.

 

그렇다면 HashMap만 있으면 되지, ConcurrentHashMap은 왜 필요할까요?

map.put("key", "abcdefg")을 하는 동안 HashMap의 내부에서는 수 많은 일이 발생합니다. 예를 들어서 map의 노드를 생성하고 저장 위치를 찾고, 기존 데이터를 최적화 하는 등등 알고리즘을 위해 여러 객체를 생성하고 조정합니다. 이런 과정을 진행중인데 누군가 map.get("key")을 동시에 호출하게 되면 아직 알고리즘이 완성되지 않은 상태에서 get()을 호출하게 됩니다. 이 경우 메모리 누수나 엉뚱한 데이터가 조회되는 문제가 발생할 수 있습니다. put()이 아직 완성되지 않은 상태에서 get()을 호출하는 것이지요.

이런 경우 동기화 된 컬렉션이나 ConcurrentHashMap을 사용하게 되면 put()이 확실하게 다 끝난 상태에서 get()이 호출되는 것을 보장해줍니다.

추가로 이런 문제는 0.0001초의 순간에 put(), get()이 동시에 발생하는 경우에 나타나기 때문에 트래픽이 적은 서비스에서는 거의 문제가 되지 않습니다. 그래도 멀티쓰레드 상황에서 동시에 Map에 접근해야 한다면 ConcurrentHashMap을 사용해서 문제를 예방하는 것이 좋겠지요^^

감사합니다.

Truestar님의 프로필

Truestar

2022.01.19

으아... 고맙습니다.