쓰레드 로컬
동시성 문제
여러개의 쓰레드나 프로세스가 동일한 데이터(공유 자원)에 접근할 때, 데이터 일관성을 해치는 문제(Inconsistency)를 동시성 문제라고 한다.
스프링은 기본적으로 모든 빈들을 싱글톤 스코프로 관리한다. 즉, 한 번 생성된 객체를 스프링 컨테이너가 재사용하도록 설계되는데, 이는 곧 해당 객체의 인스턴스가 애플리케이션에 단 1개 존재한다는 뜻이다. 때문에 인스턴스의 필드를 여러 쓰레드가 동시에 접근하게 될 경우, 동시성 문제가 발생하게 된다. 스프링 빈처럼 싱글톤 객체의 필드를 변경하며 사용할 때는 특히, 이러한 동시성 문제를 조심해야 한다.
→ 이러한 문제를 해결하기 위해 사용하는 것이 쓰레드 로컬이다.
+ 더 알아보기
이런 동시성 문제는 지역 변수에서는 발생하지 않는다. 지역 변수가 저장되는 스택 영역은 스레드를 위한 독립적인 공간이다. 따라서 프로그램 실행에 스택에 저장된 데이터를 공유하지 않고 해당 스레드에서만 독립적으로 사용된다. 즉, 아래 그림과 같이 쓰레드마다 스택 메모리가 분리되어 있다.
때문에 동시성 문제가 발생하는 곳은 같은 인스턴스 필드(주로 싱글톤에서 자주 발생), 또는 static 같은 공용 필드에 접근할 때 발생한다. 인스턴스 변수는 힙 영역에 저장되는데, 각각의 인스턴스는 독립적인 저장공간을 가지고 있기 때문에 인스턴스 변수 또한 서로 다른 메모리 공간에 할당되어 있다. 하지만 이는, 쓰레드 별로 구분되어 있다는 의미가 아니다. 여러 쓰레드가 같은 인스턴스의 변수에 접근하게 된다면 동시성 문제가 발생할 수 있는 것이다.
쓰레드 로컬이란?
쓰레드 로컬이란, 해당 쓰레드만 접근할 수 있는 특별한 저장소를 말한다. 쉽게 설명해서, 각각의 쓰레드를 인식해서 쓰레드별로 확실하게 구분된 각각 별도의 데이터 저장소를 가지는 것이다.
쓰레드 로컬을 사용하면, 각 쓰레드마다 별도의 내부 저장소를 제공한다. 따라서 같은 인스턴스의 쓰레드 로컬 필드에 접근해도 동시성 문제가 발생하지 않는다.
주의사항
쓰레드 로컬의 값을 사용 후 제거하지 않고 그냥 두면 WAS(톰캣)처럼 쓰레드 풀을 사용하는 경우에 심각한 문제가 발생할 수 있다.
사용자A가 어떤 요청을 보내는 상황을 생각해보자. WAS는 요청을 처리하기 위해, 쓰레드 풀에서 쓰레드 하나를 할당해준다. 할당된 쓰레드는 요청을 처리하기 위해 필요한 데이터들(사용자A와 관련된 데이터들)을 쓰레드 로컬에 저장한다. 이렇게 모든 요청을 수행하고 응답을 종료하게 되면, WAS는 사용이 끝난 쓰레드를 제거하는 것이 아니라 쓰레드 풀에 반환한다.(쓰레드를 생성하는 비용은 비싸기 때문에 제거하지 않고, 재사용한다.) 즉, 사용자A의 요청을 처리하기 위해 사용된 쓰레드는 쓰레드 풀에 살아있게 되고 때문에 쓰레드 로컬의 저장소를 비우지 않는다면, 사용자A의 데이터가 함께 살아있게 되는 것이다.
이후, 사용자B가 조회를 위한 새로운 HTTP 요청을 보내고 WAS에서 랜덤으로 할당된 쓰레드가 사용자A의 요청을 처리했던 쓰레드와 동일하다면, 쓰레드 로컬에서 데이터를 조회할 때, 사용자A와 관련된 값들이 반환되는 문제가 발생한다. 때문에, 요청이 끝날 때마다 쓰레드 로컬의 값을 ThreadLocal.remove()를 통해서 꼭 제거해야 한다.
'Spring' 카테고리의 다른 글
스프링 DB 1편 - 섹션 3~4 (0) | 2025.03.18 |
---|---|
스프링 DB 1편 - 섹션 1~2 (0) | 2025.03.18 |
빈으로 등록된 필터가 WebSecurity의 ignoring에 의해 무시되지 않는 이유 (0) | 2025.03.17 |
스프링 MVC 2편 - 섹션 10~11 (0) | 2025.03.17 |
스프링 MVC 2편 - 섹션 7~9 (0) | 2025.03.17 |