Language/Java

ThreadLocal

kimjingyu 2023. 6. 7. 14:20
728x90

✏️ 개요

  • 동시성 문제
    • 여러 쓰레드가 동시에 같은 인스턴스의 필드 값을 변경하면서 발생하는 문제를 동시성 문제라고 한다.
    • 스프링 빈처럼 싱글톤 객체의 필드를 변경하여 사용할 때, 이러한 동시성 문제를 조심해야 한다.
    • 동시성 문제는 지역 변수에서는 발생하지 않는다. 지역 변수는 쓰레드마다 각기 다른 메모리 영역이 할당된다.
    • 동시성 문제가 발생하는 곳은 같은 인스턴스의 필드, 또는 static 같은 공용필드에 접근할 때 발생한다.
  • ThreadLocal
    • 해당 쓰레드만 접근할 수 있는 특별한 저장소를 말한다. 즉, 각 쓰레드마다 별도의 내부 저장소를 제공한다.
    • 자바는 언어차원에서 쓰레드 로컬을 지원하기 위한 java.lang.ThreadLocal 클래스를 지원한다.

📌 사용법

  • 값 저장: ThreadLocal.set(xxx)
  • 값 조회: ThreadLocal.get()
  • 값 제거: ThreadLocal.remove()
    • 해당 쓰레드가 쓰레드 로컬을 모두 사용하고 나면, ThreadLocal.remove()를 호출해서 쓰레드 로컬에 저장된 값을 제거해주어야 한다.
private ThreadLocal<String> nameStore = new ThreadLocal<>();

public String logic(String name) {
    log.info("저장 전 name={} -> nameStore={}", name, nameStore.get());

    nameStore.set(name);
    log.info("저장");
    sleep(1000);

    log.info("조회 nameStore={}", nameStore.get());
    return nameStore.get();
}
private ThreadLocalService service = new ThreadLocalService();

@Test
void threadLocal() {
    log.info("main start");

    Runnable userA = () -> {
        service.logic("userA");
    };

    Runnable userB = () -> {
        service.logic("userB");
    };

    Thread threadA = new Thread(userA);
    threadA.setName("thread-AAA");
    Thread threadB = new Thread(userB);
    threadB.setName("thread-BBB");

    threadA.start();
    sleep(100);
    threadB.start();

    sleep(2000);
    log.info("main exit");
}

🗒️ 결과

14:17:44.723 [Test worker] INFO hello.advanced.trace.threadlocal.ThreadLocalServiceTest -- main start
14:17:44.726 [thread-AAA] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 저장 전 name=userA -> nameStore=null
14:17:44.727 [thread-AAA] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 저장
14:17:44.831 [thread-BBB] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 저장 전 name=userB -> nameStore=null
14:17:44.832 [thread-BBB] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 저장
14:17:45.732 [thread-AAA] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 조회 nameStore=userA
14:17:45.837 [thread-BBB] INFO hello.advanced.trace.threadlocal.code.ThreadLocalService -- 조회 nameStore=userB
14:17:46.836 [Test worker] INFO hello.advanced.trace.threadlocal.ThreadLocalServiceTest -- main exit

📌 ThreadLocal.remove()

  • Tomcat과 같은 WAS는 Thread Pool을 사용한다.
  • 따라서 ThreadLocal을 모두 사용하고 나면 ThreadLocal.remove()를 호출해서 ThreadLocal에 저장된 값을 제거해주어야 한다.
private void releaseTraceId() {
    TraceId traceId = traceIdHolder.get();
    if (traceId.isFirstLevel()) {
        traceIdHolder.remove();
    } else {
        traceIdHolder.set(traceId.createPreviousId());
    }
}

🔨 설정

@Configuration
public class LogTraceConfig {
    @Bean
    public LogTrace logTrace() {
        return new ThreadLocalLogTrace();
    }
}
참고

https://mangkyu.tistory.com/258

 

[Java] Thread와 Runnable에 대한 이해 및 사용법

이번에는 자바 초기부터 멀티 쓰레드 기반의 동시성 프로그래밍을 위해 만들어졌던 Thread와 Runnable를 살펴보도록 하겠습니다. 1. Thread와 Runnable에 대한 이해 및 사용법 [ 쓰레드와 자바의 멀티 쓰

mangkyu.tistory.com

 

728x90