Java & Kotlin

[Java] 스레드 로컬(ThreadLocal)과 상속 가능한 스레드 로컬( InheritableThreadLocal)에 대하여

망나니개발자 2024. 2. 6. 10:00
반응형

 

 

1. 스레드 로컬(ThreadLocal)과 상속 가능한 스레드 로컬( InheritableThreadLocal)에 대하여


[ 스레드 로컬(ThreadLocal)이란? ]

자바는 오랜 기간 동안 동시성 처리를 위해 스레드를 사용해왔다. 대표적으로 스프링 프레임워크는 멀티 스레드 모델을 사용하고 있으며, 1개의 요청을 1개의 스레드가 처리하는 thread-per-request 방식으로 동작하고 있다.

 

 

자바는 각각의 스레드 별로 필요한 정보를 저장할 수 있는 스레드 로컬(Thread Local)이라는 기술을 제공하고 있다. 각각의 스레드는 살아있는 한 ThreadLocal에 접근할 수 있는 암묵적인 참조를 갖는 것이다. 이러한 스레드 로컬은 다음과 같이 활용할 수 있다. 스레드 내에서 공유할 값을 저장하고 어디에서든지 사용할 수 있는 것이다.

class ThreadLocalMain {

    public static void main(String[] args) {
        // 스레드 내에서 공유할 데이터를 설정
        Member member = new Member();
        member.name = "Hello World";
        ThreadLocalHolder.SHARED_VALUE.set(member);

        // 스레드 내에서 데이터에 접근
        System.out.println("Thread Local value:" + ThreadLocalHolder.SHARED_VALUE.get().name);
    }
}

class ThreadLocalHolder {
    static ThreadLocal<Member> SHARED_VALUE = new ThreadLocal<>();
}

class Member {
    String name;
}

 

 

참고로 스프링의 데이터베이스 접근 기술은 스레드 로컬을 기반으로 동작하고 있다. 서비스 계층에서 트랜잭션을 시작하도록 커넥션을 커넥션 풀로부터 꺼내오고, Dao 계층에서 해당 커넥션을 활용해 커밋하거나 롤백하는 동작은 스레드 로컬 덕분에 가능한 것이다.

 

 

스프링의 TransactionSynchronizationManager 클래스를 보면 스레드 로컬 기반으로 트랜잭션 속성을 저장하여 활용하고 있음을 알 수 있다.

public abstract class TransactionSynchronizationManager {

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");

	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");

	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");

	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");

	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");

}

...

 

 

일반적인 멀티스레드 기반의 애플리케이션에서는 동일한 스레드가 사용 후에 스레드 풀로 반납되어 재사용되는 형태이므로, 특정 사용자에 대한 민감한 정보는 스레드가 반납될 때 반드시 초기화해줄 필요가 있다.

 

 

 

 

[ 상속 가능한 스레드 로컬(InheritableThreadLocal)이란? ]

요청을 처리하는 스레드가 오랜 특정 작업 때문에 기간 동안 점유되어 스레드 풀로 반환되지 않는다면 동시성이 떨어지게 된다. 이러한 문제를 해결하기 위해 요청의 작업들 중 일부는 비동기로 실행되도록 백그라운드 스레드로 위임시킬 수 있다. 그러면 백그라운드 스레드에서는 스레드 로컬에 저장된 값이 없게 되므로 문제가 생길 수 있으므로, 자바는 자식 스레드에게 스레드 로컬의 값을 위임시켜주는 상속 가능한 스레드 로컬(InheritableThreadLocal)을 제공하고 있다.

InheritableThreadLocal은 ThreadLocal을 상속받는 클래스로, 부모 스레드에서 자식 스레드로의 값을 상속시킨다. 자식 스레드가 생성되면, 자식은 부모가 갖는 값을 전달받는다. 일반적으로 자식의 값은 부모의 값과 동일하지만, childValue 메서드를 재정의하면 자식의 값을 부모의 임의의 함수로 만들 수도 있다. InheritableThreadLocal은 일반 스레드 로컬 변수보다 우선시된다.

이러한 상속 가능한 스레드 로컬은 다음과 같이 활용할 수 있다.

class InheritableThreadLocalMain {

    public static void main(String[] args) throws InterruptedException {
        // 부모 스레드에서 공유할 데이터를 설정
        Member member = new Member();
        member.name = "Hello World";
        ThreadLocalHolder.SHARED_VALUE.set(member);

        // 자식 스레드 생성
        Thread childThread = new Thread(() -> {
            // 자식 스레드에서 데이터를 변경
            Member childThreadMember = ThreadLocalHolder.SHARED_VALUE.get();
            childThreadMember.name = "Hello MangKyu";
            ThreadLocalHolder.SHARED_VALUE.set(childThreadMember);

            System.out.println("Child Thread (After Change): " + ThreadLocalHolder.SHARED_VALUE.get().name);
        });

        childThread.start();
        Thread.sleep(1000L);

        // 부모 스레드에서 데이터에 접근
        System.out.println("Thread Local value:" + ThreadLocalHolder.SHARED_VALUE.get().name);
    }
}

class ThreadLocalHolder {
    static InheritableThreadLocal<Member> SHARED_VALUE = new InheritableThreadLocal<>();
}

class Member {
    String name;
}

 

 

자바는 객체의 참조를 복사하여 전달한다. 따라서 부모 스레드와 자식 스레드가 갖는 객체의 참조는 복사되어 존재하지만, 동일한 힙 영역의 객체를 가리키고(points to) 있으므로 자식 스레드에서 값을 변경하는 것이 부모에게도 영향을 줄 수 있으므로 주의가 필요하다. 관련된 자세한 내용은 해당 포스팅을 참고하도록 하자.

 

 

 

참고 자료

 

 

 

반응형