객체를 동기화하지 않고도 thread safe하게 사용할 수 있는 방법에는 스레드 한정, 불변 immutable 등이 있다.
ThreadLocal은 스레드와 객체를 연결해 스레드 한정 기법을 적용할 수 있는 클래스이다.
스레드 한정 기법이란, 특정 객체를 특정한 단일 스레드에서만 사용하게 하면 thread safe를 확보할 수 있다. 객체 인스턴스를 특정 스레드에 한정시켜주면 해당하는 객체가 자동으로 thread safe를 확보하게 된다.
ThreadLocal 클래스의 get 메소드를 호출하면 현재 실행 중인 스레드에서 최근에 set 메서드를 호출해 저장했던 값을 가져올 수 있따. 개념적으로 생각해보면 Map<Thread, T>로 스레드 별 객체가 저장되어있는 것이라 생각하면 될 듯 하다.
ThreadLocal은 전역 변수나 변경 가능한 싱글톤 등을 기반으로 설계되어 있는 구조에서 변수가 임의로 공유되는 상황을 막기 위해 사용하는 경우가 많다.
대표적으로 트랜잭션, 커넥션풀과 같은 곳에서 사용된다.
스레드 별로 트랜잭션 컨텍스트를 두어, 스레드 단위로 트랜잭션 컨텍스트를 관리할 수 있다.
스프링에서 사용하는 @Transactional 노테이션의 경우, ThreadLocal 객체에 트랜잭션 상태를 저장한다. 현재 메서드가 실행되고 있는 스레드에서 TransactionSynchronizationManager를 통해 트랜잭션의 상태를 알 수 있다.
또한, 커넥션 풀에서 스레드들은 각자의 커넥션을 가져와 thread safe하게 사용할 수 있다.
스레드별로 값을 관리할 수 있지만
객체 간에 눈에 보이지 않는 연결관계를 만들어내기 쉽기 때문에 애플리케이션에 어떤 영향을 미치는지 정확하게 알고 신경 써서 사용해야 한다.
위에서 얘기한대로 @Transactional은 ThreadLocal 객체 안에 트랜잭션의 상태를 저장한다. 이 @Transactional은 @Async와 같이 쓸 때 주의해야한다.
왜냐하면 @Async와 @Transactional은 서로 다른 스레드에서 동작하게 되므로 비동기 작업을 트랜잭션 안에서 다룰 수 없고, 트랜잭션이 연장되지 않는다.