본문 바로가기
Spring

Thread Per Request 모델과 ThreadLocal

by S2채닝S2 2023. 4. 6.

Spring Security Essentials

Thread Per Request 모델과 ThreadLocal

Thread Per Request 모델 개요

  • WAS는 ThradPool을 생성함 (Tomcat 기본값 200)
  • HTTP 요청이 들어오면 Queue에 적재되고, ThreadPool 내의 특정 Thread가 Queue에서 요청을 가져와 처리하게됨
  • HTTP 요청은 처음부터 끝까지 동일한 Thread에서 처리됨
  • HTTP 요청 처리가 끝나면 Thread는 다시 ThreadPool에 반납됨
  • 즉, WAS의 최대 동시 처리 HTTP 요청의 갯수는 ThreadPool의 갯수와 같음
  • Thead 갯수를 늘리면 동시 처리 갯수가 늘어나지만, Thread Context 스위칭에 의한 오버헤드도 커지기 때문에 성능이 선형적으로 증가하지는 않음
    • Thread도 CPU의 연산을 소모하는 자원이기 때문에 스레드 갯수가 많아질 수록 스레드 그 자체를 관리하는 오버헤드도 커지게 되고 성능이 스레드 풀의 스레드 개수에 비례해서 올라가지 않는다.

 

  • 최근 소개된 WebFlux 같은 기술은 Thread 갯수를 작은 갯수로 유지하며 HTTP 요청을 동시 처리 할 수 있도록 함
  • WebFlux: 최대한 적은 수의 Thread로 최대한 많은 수의 요청을 처리
    • 하나의 요청이 완료될 때까지 다수의 Thread에서 처리될 수 있음
    • HTTP 요청은 하나 이상의 Thread에 바인딩되어 처리될 수 있음
    • Therad per request 모델과는 지향하는 바가 다름

 ThreadLocal

  • Thread 범위 변수
    • 동일 Thread 내에서는 언제든 ThreadLocal 변수에 접근할 수 있음(읽고 쓸 수 있음.)

예제 코드

// TheadLocal 변수를 생성
ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();
// ThreadLocal 변수에 값 쓰기
threadLocalValue.set(1);
// ThradLocal 변수에서 값 읽기
Integer result = threadLocalValue.get();
// ThreadLocal 변수 값 제거
threadLocal.remove();
public class ThreadLocalApp {

    final static ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();

    public static void main(String[] args){
        System.out.println(getCurrentThreadName() + " ### main set value = 1"); // 1
        threadLocalValue.set(1);

        //main thread 에서 threadLocal 변수 1에 접근.
        a(); // main ### a() get value = 1   -> main 쓰레드의 메소드 a()에서 threadLocal value 에 접근
        b(); // main ### b() get value = 1   -> main 쓰레드의 메소드 b()에서 threadLocal value 에 접근

        //runAsync(): 메인 thread 가 아닌 다른 thread 에서 수행되도록 하는 람다 코드블럭.
        CompletableFuture<Void> task = runAsync(() -> {
            a(); // 다른 thread 에서 접근했으므로 null, thread 이름도 main이 아니다.
            b(); // a와 마찬가지.
        });

        task.join();
    }

    public static void a(){
        Integer value = threadLocalValue.get();
        System.out.println(getCurrentThreadName() + " ### a() get value = " + value);
    }

    public static void b(){
        Integer value = threadLocalValue.get();
        System.out.println(getCurrentThreadName() + " ### b() get value = " + value);
    }

    public static String getCurrentThreadName(){
        return Thread.currentThread().getName();
    }
}
  • 즉, 동일 Thread내에서 실행되는 Controller, Service, Repository, 도메인 모델 어디에서든 명시적인 파라미터 전달 필요없이 ThreadLocal 변수에 접근할 수 있음
  • ThreadPool과 함께 사용하는 경우 Thread가 ThreadPool에 반환되기 직전 ThreadLocal 변수 값을 반드시 제거해야함
  • 그렇지 않을 경우 아래와 같은 상황이 발생하고, 미묘한 버그가 생겨날 수 있음
    • 요청을 처리하기 위해 ThreadPool에서 Thread를 하나 가져옴
    • 요청 처리에 필요한 변수를 ThreadLocal에 set함
    • 요청 처리가 완료되고 Thread는 ThreadPool에 반환됨
    • 다른 요청을 처리하기 위해 ThreadPool에서 Thread를 하나 가져왔는데 이전 요청 처리에 사용된 ThreadLocal 변수가 남아있고, 이를 참조하여 잘못된 동작을 수행할 수 있음

 

  •  

최근댓글

최근글

skin by © 2024 ttuttak