ThreadLocal은 스레드 로컬 변수 저장소를 제공하는 기능입니다.
각 스레드가 독립적으로 값을 가질 수 있습니다.
어떤 식으로 데이터를 관리하기에 ThreadLocal이 각 스레드에 독립적인 저장 공간을 제공하는지 궁금해 get(), set(), remove()의 동작 과정을 분석했습니다.
ThreadLocal의 내부 클래스입니다.
각 스레드가 고유하게 가지며, 스레드 별로 독립적인 값을 가지는 데 사용됩니다.
Thread의 필드로 가지고 있는 모습을 확인할 수 있습니다.
이름에 Map이 포함되어있지만, 컬렉션의 Map을 구현하지는 않은 클래스입니다.
ThreadLocalMap은 현재 동작중인 스레드를 해싱 처리해 table의 인덱스를 지정하고, Entry[]에서 인덱스에 지정한 요소를 저장합니다.
ThreadLocalMap에 값을 저장할 때 사용하는 클래스입니다.
ThreadLocalMap 내부 클래스로 정의되어 있습니다.
ThreadLocalMap의 필드인 table로 관리됩니다.
ThreadLocalMap의 생성자에서 기본 크기인 16으로 생성합니다.
Entry는 위에서 선언하는 것처럼 key로는 ThreadLocal을 가지고, value로는 set()으로 지정한 파라미터를 가집니다.
Entry에서 자주 사용하는 메서드 refersTo()는 Reference의 메서드로, 해당 참조의 값이 객체인지 아닌지(= null인지) 확인하는 용도로 사용됩니다.
canonicalizing mappings을 구현하기 위해 사용되는 클래스로, ThreadLocalMap에서 데이터를 관리하는 Entry가 상속하고 있는 클래스입니다.
강한 참조의 반대로, 객체에 대한 참조가 존재하더라도 필요에 의해 GC의 대상이 될 수 있도록 하는 기능입니다.
WeakReference를 상속한 객체는 JVM의 GC가 메모리를 회수할 필요하고, 다른 곳에서 강한 참조가 없다면 GC의 대상이 됩니다.
ThreadLocal에서 데이터를 사용한 이후 clear 하지 않을 때를 대비한 기능이라고 볼 수 있습니다.
특정 TerminatingThreadLocal 인스턴스에 바인딩된 값을 가진 스레드가 종료될 때 호출되는 기능을 가지고 있는, ThreadLocal의 확장입니다.
네이티브 바이트 버퍼, 네이트 버퍼의 스레드 로컬 캐시를 정리하기 위해 JDK 내부에서 사용됩니다.
carrier 스레드에서만 사용된다는 특징을 가지고 있습니다.
다음과 같은 순서로 동작합니다.
Thread.currentThread()의 threadLocals를 반환합니다.
다음과 같은 순서로 동작합니다.
null을 반환합니다.
ThreadLocalMap이 존재하지 않거나, 존재하더라도 해당 스레드에서 ThreadLocal에 저장한 데이터가 없을 경우에만 동작하는 메서드입니다.
protected 접근제어자를 통해, ThreadLocal을 커스터마이징할 때 초기값을 자유롭게 세팅할 수 있도록 한 메서드입니다.
Thread.currentThread()의 threadLocals를 초기화합니다.
get() 메서드의 동작을 요약하자면 다음과 같습니다.
여기서 가장 큰 특징은 get()임에도 불구하고 threadLocals를 초기화하는 로직이 존재한다는 점입니다.
이는 지연 초기화(lazy initialization)로, ThreadLocal에서 get()을 호출했다는 의미는 스레드에서 ThreadLocal을 사용할 것이 분명하기 때문에 set()을 하지 않았더라도 threadLocals를 초기화해주기 위함이라고 생각합니다.
다음으로 set() 메서드를 살펴보도록 하겠습니다.
get()에 비해 간단한 모습을 확인할 수 있습니다.
동작 과정은 다음과 같습니다.
동작 과정은 다음과 같습니다.
전부 삭제하기 위해 사용되는 내부 remove() 메서드의 경우 Entry[]에 저장된 값을 모두 제거하기 위해 ThreadLocalMap.set()과 유사한 과정을 수행하고 있음을 확인할 수 있습니다.
스프링에서 트랜잭션을 관리하는 추상 클래스입니다.
ThreadLocal을 많이 사용하는 것에서 알 수 있듯이, 멀티 스레드 환경에서 트랜잭션 리소스를 스각 스레드마다 독립적으로 관리하는 기능을 제공합니다.
여기서 특히 중요한 필드는 resources와 synchronizations 입니다.
resources는 바인딩된 리소스를 관리하는 필드입니다.
key로는 DataSource와 같은 트랜잭션 리소스를 식별할 수 있는 객체를 지정합니다.
value로는 실제 트랜잭션 리소스를 지정합니다.
JDBC의 경우 key = DataSource, value = Connection입니다.
synchronizations는 트랜잭션 동기화 콜백을 관리하는 TransactionSynchronization를 저장하는 필드입니다.
TransactionSynchronization는 트랜잭션 생명 주기 이벤트에 대한 콜백 메서드를 정의해 커밋/롤백될 때 호출할 수 있습니다.
이를 통해 전처리/후처리가 가능합니다.
RequestContextHolder는 스레드에 바인딩된 웹 관련 컨텍스트 정보를 관리하는 기능을 제공합니다.
RequestAttributes를 저장하는 ThreadLocal을 사용합니다.
requestAttributesHolder 필드의 경우 현재 스레드에 바인딩된 RequestAttributes를 저장합니다.
그러므로 현재 웹 관련 컨텍스트 정보를 저장할 수 있습니다.
inheritableRequestAttributesHolder 필드의 경우 부모 스레드에 저장된 웹 관련 컨텍스트 정보를 관리합니다.
부모 스레드에서 웹 관련 컨텍스트 정보가 필요한 경우 사용합니다.
LocaleContextHolder는 현재 스레드의 Locale 정보를 관리합니다.
RequestContextHolder와 유사한 구조로, localeContextHolder 필드는 현재 스레드의 Locale 정보를 관리하고 inheritableLocaleContextHolder는 부모 스레드의 Locale 정보에 접근할 수 있을 때 사용합니다.