-
Spring 핵심 원리 고급편 - 2. ThreadLocalSpring-Boot/스프링핵심원리 - 고급편 2022. 6. 2. 13:31
필드 동기화
* 동시성 문제 *
FieldLogTrace는 싱글톤으로 등록된 스프링 빈이다. 이 객체의 인스턴스가 애플리케이션에 딱 1 존재한다는 뜻이다. 이렇게 하나만 있는 인스턴스의 ' FieldLogTrace.traceIdHolder ' 필드를 여러 쓰레드가 동시에 접근하기 때문에 문제가 발생
- 실무에 한번 나타나면 개발자를 가장 괴롭히는 문제도 바로 이러한 동시성 문제이다.
참고
Runnable user A = new Runnable() { @Override public void run() { } }
Runnable userA = () -> { }
: 동일 코드!
* 동시성 문제 *
- 여러 쓰레드가 동시에 같은 인스턴스의 필드 값을 변경하면서 발생하는 문제를 동시성 문제라 한다.
- 이런 동시성 문제는 여러 쓰레드가 같은 인스턴스의 필드에 접근해야 하기 때문에 트래픽이 적은 상황에서는 확율상 잘 나타나지 않고, 트래픽이 점점 많아질수록 자주 발생한다.
- 특히 스프링 빈처럼 싱글톤 객체의 필드를 변경하며 사용할 때 이러한 동시성 문제를 조심해야 한다.
* 참고 *
- 이런 동시성 문제는 지역 변수에서는 발생하지 않는다.
- 지역 변수는 쓰레드마다 각각 다른 메모리 영역이 할당된다
- 동시성 문제가 발생하는 곳은 같은 인스턴스의 필드 ( 주로 싱글톤에서 자주 발생 ), 또는 static 같은 공용 필드에 접근할 때 발생한다.
- 동시성 문제는 값을 읽기만 하면 발생하지 않는다. 어디선가 값을 변경하기 때문에 발생한다.
ThreadLocal
- 쓰레드 로컬은 해당 쓰레드만 접근할 수 있는 특별한 저장소를 말한다.
- 사용자를 인식해서 사용자별로 확실하게 구분
* 일반적인 변수 필드 *
- 여러 쓰레드가 같은 인스턴스의 필드에 접근하면 처음 쓰레드가 보관한 데이터가 사라질 수 있다.
* 쓰레드 로컬 *
- 쓰레드 로컬을 사용하면 각 쓰레드마다 별도의 내부 저장소를 제공한다. 따라서 같은 인스턴스의 쓰레드 로컬 필드에 접근해도 문제 없다.
- 자바는 언어차원에서 쓰레드 로컬을 지원하기 위한 'java.lang.TheadLocal' 클래스를 제공한다.
* ThreadLocal 사용법 *
ㆍ값 저장 : ThreadLocal.set(xxx)
ㆍ값 조회 : ThreadLocal.get( )
ㆍ값 제거 : ThreadLocal.remove( )
* 주의 *
: 해당 쓰레드가 쓰레드 로컬을 모두 사용하고 나면 ' ThreadLocal.remove( )' 를 호출해서 쓰레드 로컬에 저장된 값을 제거해주어야한다.
쓰레드 로컬 - 주의사항
- 쓰레드 로컬의 값을 사용 후 제거하지 않고 그냥 두면 WAS(톰캣)처럼 쓰레드 풀을 사용하는 경우에 심각한 문제가 발생할 수 있다
* 사용자A 저장 요청 *
1. 사용자A가 저장HTTP를 요청했다
2. WAS는 쓰레드 풀에서 쓰레드를 하나 조회한다,
3. 쓰레드 'thread-A'가 할당되었다
4. thread-A 는 ' 사용자A '의 데이터를 쓰레드 로컬에 저장한다.
5. 쓰레드 로컬의 thread-A 전용 보관소에 사용자A 데이터를 보관한다.
* 사용자A 저장 요청 종료 *
1. 사용자A의 HTTP 응답이 끝난다
2. WAS는 사용이 끝난 'thread-A' 를 쓰레드 풀에 반환한다. 쓰레드를 생성하는 비용은 비싸기 때문에 쓰레드를 제거하지 않고, 보통 쓰레드 풀을 통해서 쓰레드를 재사용한다.
3. thread-A 는 쓰레드 풀에 아직 살아있다. 따라서 쓰레드 로컬의 thread-A 전용 보관소에 사용자A 데이터도 함께 살아있게 된다.
- 사용자 B가 조회를 위한 새로운 HTTP 요청을 하면 WAS는 쓰레드 풀에서 쓰레드 하나를 조회
- 제거하지 않은 thread-A가 할당될 경우 사용자A 값을 반환!
→ 이런 문제를 예방하기 위해 요청이 끝나면 쓰레드 로컬 값을 꼭 제거 해야한다!
( WAS에서 나갈때 필터나 인터셉터에서 클리어할 것! )
출처 : 인프런 김영한님의 스프링 핵심원리 - 고급편
'Spring-Boot > 스프링핵심원리 - 고급편' 카테고리의 다른 글
Spring 핵심 원리 고급편 - 7. 빈 후처리기 (0) 2022.06.08 Spring 핵심 원리 고급편 - 6. 스프링이 지원하는 프록시 (0) 2022.06.07 Spring 핵심 원리 고급편 - 5. 동적 프록시 기술 (0) 2022.06.07 Spring 핵심 원리 고급편 - 4. 프록시 패턴과 데코레이터 패턴 (0) 2022.06.03 Spring 핵심 원리 고급편 - 3. 템플릿 메서드 패턴과 콜백 패턴 (0) 2022.06.02