Thread Local 개념과 내부 구조

Thread Local 이란?

 

Spring 의 패턴으로 예를 들때 Layer간 어떠한 값을 접근하기위해서는 다양한 방법이 존재한다.

(더 다양한 방법이 있겠지만 2가지만 제시해보겠다.)

 

(1) Layer간 메서드 Paramter로 넘겨서 접근하기

(2) 클래스의 public static 멤버 변수로 선언해서 접근하기

 

위의 두 방법은 다음의 문제에 직면할 수 있다.

 

(1) Layer간 메서드 Paramter로 넘겨서 접근하기

  • 다수의 Layer에서 공통적으로 사용하는 어떠한 값이 있다면 Layer 객체간 소통을 할 때 이 어떠한 값을 parameter로 넘겨야하며 파라미터 중복코드가 다수 발생될 수 있다.
  • Layer간 각 Layer가 책임질 필요 없는 parameter를 받게될 경우가 생길 수 있다.

(2) 클래스의 public static 멤버 변수로 선언해서 접근하기

  • Layer간 Parameter를 넘겨서 접근하는 방법보다는 더 효율적이지만 해당 값이 mutable 하다면 멀티스레드 환경에서 동시성 문제에 직면할 수 있다.

 

Thread Local 은 Layer간 현재 수행중인 Thread 한정의 local 변수를 선언해 동일한 스레드 내의 처리라면 어느 Layer 든 local 변수에 접근이 가능하게 해주는 Thread의 local 변수를 컨트롤하는 Handler 클래스라고 보면 된다.  local 변수이기 때문에 메서드 블록이 끝나면 제거되는 일반적인 local 변수와 비슷하게 현재 수행중이던 스레드가 종료되면 Thread Local 변수도 삭제된다.

 

(단, 일반적으로 Thread pool을 사용하는 WAS 환경에서는 Thread 를 사용완료 후 Thread는 종료되지 않고 pool에 반납만 되기 때문에 Thread 사용이 완료됐다면 Thread local 변수를 다음 요청을 위해서 초기화 시켜줘야한다.)

 

대략적인 구조의 모습은 다음과 같다.

 

 

결론적으로 Layer간 parameter로 넘겨 객체간 소통할 필요도 없고 각 스레드 한정 local 변수이기 때문에 동시성 문제에 직면하지도 않게된다.

 

 

Thread Local 내부 구조

 

 

class Thread implements Runnable {

    ...
    
    
    ThreadLocal.ThreadLocalMap threadLocals = null; //Thread 한정 모든 Layer에서 ThreadLocal를 통해 접근할수 있게 참조 변수 선언.


    ...

}

 

public class ThreadLocal<T> {

    ...

    ThreadLocalMap getMap(Thread t) { // Tread 한정 local 변수로 사용될 수 있는 ThreadLocalMap 변수를 가져온다.
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) { // 현재 수행중인 Thread 내부 Map이 초기화가 되어있지 않다면 초기화 후 value를 put 한다.
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }


    public void set(T value) {
        Thread t = Thread.currentThread(); // 현재 수행 중인 Thread의 정보를 가져온다.
        ThreadLocalMap map = getMap(t); 
        if (map != null)                                   
             map.set(this, value);         // 현재 ThreadLocal 객체를 key로 사용해 ThreadLocalMap에 value를 put 한다. 내부적으로 현재 ThreadLocal이 가진 hash code를 사용한다.
        else
            createMap(t, value);                      
    }

    public T get() {                       // 현재 수행중인 Thread의 내부  ThreadLocalMap 변수를 가져와 현재 ThreadLocal 객체를 key로 사용해 값을 조회한다.
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }


    public void remove() {  // 현재 수행중인 스레드의 내부  ThreadLocalMap 변수를 가져와 put 되어있던 값을 초기화한다.
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
   }

   ...
   
}

 

 

 

기존에 어디에 활용되고 있었을까?

 

ThreadLocal은 어느 Layer 든 간단하게 HttpServletRequest 를 조회 할 수 있는 RequestContextHolder 클래스가 내부적으로 선언하여 사용하고있다.

 

public abstract class RequestContextHolder {
    
    ...
    
    private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
    private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");

    ...
    
}