새소식

반응형
Java

[Java] ThreadLocal이란

  • -
반응형

ThreadLocal이란

ThreadLocal은 JDK 1.2부터 제공된 오래된 클래스입니다. 

ThreadLocal 클래스를 활용하면 스레드 단위로 로컬 변수를 사용할 수 있기 때문에 마치 전역 변수처럼 여러 메서드에서 활용할 수 있습니다.

 

ThreadLocal 클래스는 thread-local 변수들을 제공합니다. 

이 변수들은 각 스레드가 독립적으로 변수의 초기화 된 사본을 가지고 있습니다. 

ThreadLocal 인스턴스들은 보통 스레드와 상태를 연결하려고 하는 클래스들의 private static 필드들입니다.

(예를 들어, 유저 ID 또는 트랜잭션 ID)

 

제공되는 메서드

set, get, remove - threadLocal의 로컬 변수 조작 

withInitial - static 메서드를 이용한 객체 생성

 

활용 방안( 예시 ) 

객체 공유 방안 ( 파라미터, 전역 변수..)

ThreadLocal은 하나의 Thread에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 주기 때문에 

Thread와 관련된 코드에서 파라미터를 사용하지 않고 객체를 전파하기 위한 용도로 주로 사용됩니다. 

주요 용도는 다음과 같습니다.

 

사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전파합니다.

트랜잭션 컨텍스트 전파 - TransactionManager는 TransactionContext를 전파하는 데 ThreadLocal을 사용합니다.

스레드에 안전해야 하는 데이터 보관

이 외에도 Thread 기준으로 동작해야 하는 기능을 구현할 때 ThreadLocal을 유용하게 사용할 수 있습니다.

 

SpringSecurity의 SecurityContextHolder

DispatcherServlet을 통해 요청이 들어오면, 요청을 받은 Thread별 인증 정보가 ThreadLocal에 보관되고,

Thread 내에서 계속 공유가 된다.

 

사용 시 주의사항

Thread를 생성하는 비용이 비싸기 때문에, Thread를 미리 만들어 pool을 생성해 재활용합니다.

ThreadLocal을 사용 시, 다 사용된 값은 비워주어야 다음에 Thread 동작에 오동작을 방지할 있습니다. 

 

public class ThreadLocalTest {

    //    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "str");
    private String threadLocal_str = "";

    public void test_thread_local(String str) {
        threadLocal.set(str);
        ThreadLocalTest.sleep(1000);
        System.out.println("ThreadLocal Test : " + threadLocal.get());
        threadLocal.remove();
    }

    public void test(String str) {
        threadLocal_str = str;
        ThreadLocalTest.sleep(1000);
        System.out.println("ThreadLocal-Normal Test : " + threadLocal_str);
    }


    public static void main(String[] args) {
//        normalTest();
        threadLocalTest();
    }

    private static void threadLocalTest() {
        ThreadLocalTest localTest = new ThreadLocalTest();
        Runnable one = () -> localTest.test_thread_local("test1");
        Runnable two = () -> localTest.test_thread_local("test2");

        Thread thread1 = new Thread(one);
        thread1.setName("thread1");
        Thread thread2 = new Thread(two);
        thread2.setName("thread2");

        thread1.start();
        ThreadLocalTest.sleep(100);
        thread2.start();
        ThreadLocalTest.sleep(3000);
    }

    private static void normalTest() {
        ThreadLocalTest localTest = new ThreadLocalTest();
        Runnable one = () -> localTest.test("test1");
        Runnable two = () -> localTest.test("test2");

        Thread thread1 = new Thread(one);
        thread1.setName("thread1");
        Thread thread2 = new Thread(two);
        thread2.setName("thread2");

        thread1.start();
        ThreadLocalTest.sleep(100);
        thread2.start();
        ThreadLocalTest.sleep(3000);
    }

    private static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.