Java

[Java] ThreadLocal

Hyung1 2021. 1. 24. 05:32
728x90
반응형

ThreadLocal

Oracla Docs에서는 ThreadLocal 클래스를 다음과 같이 설명한다.

 

ThreadLocal 클래스는 thread-local 변수들을 제공한다. 이 변수들은 get 또는 set 메소드를 통해 접근하는 각 스레드가 독립적으로 변수의 초기화 된 사본을 가지고 있다는 점에서 다르다. ThreadLocal 인스턴스들은 보통 스레드와 상태를 연결하려고 하는 클래스들의 private static 필드들이다. (예를들어, 유저 ID 또는 트랜잭션 ID)

 

일반 변수의 수명은 특정 코드 블록(예, 메서드 범위, for 블록 범위 등) 범위 내에서만 유효하다.

{
    int a = 10;
    ...
   // 블록 내에서 a 변수 사용 가능
}
// 변수 a는 위 코드 블록이 끝나면 더 이상 유효하지 않다. (즉, 수명을 다한다.)

 

반면에 ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 그 쓰레드에 설정된 변수 값을 사용할 수 있게 된다. 아래 그림은 쓰레드 로컬 변수가 어떻게 동작하는 지를 간단하게 보여주고 있다.

위 그림에서 주목할 점은 동일한 코드를 실행하는 데, 쓰레드1에서 실행할 경우 관련 값이 쓰레드1에 저장되고 쓰레드2에서 실행할 경우 쓰레드2에 저장된다는 점이다.

 

 

좀 더 쉽게 말하자면, ThreadLocal 변수를 선언하면 멀티 스레드 환경에서 각 스레드마다 독립적인 변수를 가지고, get(), set() 메소드를 통해 값에 대해 접근할 수 있다. 간단한 코드로 예를 들어보자.

private static final ThreadLocal<Integer> threadLocalId = new ThreadLocal<>();

위와 같이 threadLocalId 변수를 선언하고, threadLocalId.set(1) 으로 값을 지정해주면, 지정해준 스레드 안에서 threadLocalId.get()을 호출했을 때 지정해준 값인 1을 리턴한다.

각 스레드는 스레드가 종료되기 전까지 ThreadLocal 인스턴스를 참조한다.

ThreadLocal의 활용

ThreadLocal은 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 주기 때문에 쓰레드와 관련된 코드에서 파라미터를 사용하지 않고 객체를 전파하기 위한 용도로 주로 사용되며, 주요 용도는 다음과 같다.

  • 사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전파한다.
  • 트랜잭션 컨텍스트 전파 - 트랜잭션 매니저는 트랜잭션 컨텍스트를 전파하는 데 ThreadLocal을 사용한다.
  • 쓰레드에 안전해야 하는 데이터 보관
  • InheritableThreadLocal이라는 ThreadLocal을 상속받아 구현한 클래스를 사용한다면 단일 쓰레드 뿐만 아니라 그 쓰레드에서 생성한 하위 쓰레드 까지도 데이터를 공유해서 사용할 수가 있다.

ThreadLocal 사용시 주의 사항

쓰레드 풀 환경에서 ThreadLocal을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해 주어야 한다. 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있다.

 

예를 들면 ThrreadLocal 객체를 이용해 set() 메서드로 현재 쓰레드의 로컬 변수에 값을 저장한 후, get() 메서드를 이용해 로컬 변수 값을 읽어 왔다고 가정했을때, 데이터의 사용이 끝나면 ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 반드시 삭제해줘야 한다.

ThreadLocal의 동작방식

TrheadLocal 변수는 Thread 클래스와 밀접하게 관련있다. Thread.current() 메소드를 호출하면 현재 실행중인 내 쓰레드에 대한 정보를 담고 있는 Thread 객체가 리턴된다. ThreadLocal 변수에 대한 정보는 이 Thread 객체에 달려있다. Thread 클래스를 열어보면

ThreadLocal.TrheadLocalMap threadLocals = null; 

threadLocals 라는 변수가 달려있다. 쓰레드가 코드를 실행하면서 만나게 되는 모든 ThreadLocal 변수들은 여기에 정보가 달린다.

ThreadLocalMap은 최초의 16개의 entry를 갖는 배열(Entry[])로 선언되며 사용되는 ThreadLocal 변수의 양이 늘어나면 확장된다. ThreadLocalMap의 Key로는 ThreadLocal 변수에 대한 WeakReference가 사용되며, GC에 의해 ThreadLocal 변수가 회수되면 일련의 동작에 따라 ThreadLocalMap에서도 회수된다. (자바에서 entry란 키와 값으로 구성되는 것을 엔트리(entry)라고 한다.)

 

 

각 쓰레드는 ThreadLocalMap의 Traverse 시작점으로 특정 해시값의 Modular map.length 값을 가지고 있으며 원하는 Key를 찾을 때까지 배열 인덱스를 하나씩 증가시키며 찾는다.

 

ThreadLocal 변수는Thread 객체에 정보를 달아놔서 쓰레드가 동기화를 원척적으로 필요없게 만들어 놓은 방법이다.

 

정리

  • ThreadLocal 변수를 선언하면 각 스레드가 별도의 변수처럼 사용할 수 있다.
  • 쓰레드가 종료되기 전까지 변수를 사용할 수 있다.
  • 스레드풀을 통해 스레드를 재사용하는 경우 이전에 사용했던 값을 공유할 수 있다.

 

출처:

경자 프로젝트

자바캔

skygl.log

728x90
반응형