ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring 핵심 원리 고급편 - 2. ThreadLocal
    Spring-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에서 나갈때 필터나 인터셉터에서 클리어할 것! )

     

     

     

    출처 : 인프런 김영한님의 스프링 핵심원리 - 고급편

    https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B3%A0%EA%B8%89%ED%8E%B8

     

     

    댓글

Designed by Tistory.