Programming/JAVA

🚀 ThreadLocal, 이거 모르고 멀티스레드 프로그래밍 한다고?! 🤯

철부지개발자 2025. 3. 2. 10:29
반응형

"내 변수 값이 왜 계속 바뀌죠?" 😭

멀티스레드 환경에서 개발하다 보면
"어? 값이 이상하게 바뀌네?"
"이 변수 왜 다른 스레드에도 공유되지?"
이런 황당한 상황을 한 번쯤 겪었을 겁니다.

이럴 때 딱 필요한 게 바로 ThreadLocal입니다!

오늘은 ThreadLocal이 무엇인지, 언제 어떻게 사용하는지
쉽고 빠르게 이해할 수 있도록 정리해드릴게요. 🚀


🧐 ThreadLocal이 뭐길래?

ThreadLocal은 각 스레드마다 독립적인 변수를 저장할 수 있는 기능입니다.
보통 공유 변수는 여러 스레드에서 동시에 접근해서 값이 꼬일 수 있는데,
ThreadLocal을 쓰면 각 스레드가 자기만의 값을 가짐! 🎯

💡 한 줄 요약:
"각 스레드가 자기 전용 변수를 가질 수 있게 해주는 기능!"


📌 ThreadLocal 기본 사용법 (초간단 예제)

public class ThreadLocalExample {
    // ThreadLocal 인스턴스 생성
    private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Runnable task = () -> {
            threadLocalValue.set((int) (Math.random() * 100));  // 각 스레드마다 다른 값 저장
            System.out.println(Thread.currentThread().getName() + " : " + threadLocalValue.get());
        };

        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");

        t1.start();
        t2.start();
    }
}

✅ ThreadLocal.withInitial(() -> 0); → 기본값 0으로 설정
✅ threadLocalValue.set(값); → 각 스레드에서 자기만의 값 저장
✅ threadLocalValue.get(); → 저장한 값 가져오기

🔥 실행하면?
각 스레드가 자기만의 값을 가지는 걸 확인할 수 있음!


🎯 ThreadLocal을 언제 써야 할까?

💡 ThreadLocal이 필요한 순간들!

1️⃣ 각 스레드가 독립적인 값을 가져야 할 때

  • HTTP 요청 ID, 트랜잭션 ID 등 스레드별로 다른 값 관리

2️⃣ 멀티스레드 환경에서 안전한 데이터 저장

  • 스프링에서 @Transactional이 Connection을 ThreadLocal에 저장하는 방식

3️⃣ 서블릿 필터에서 사용자 정보 저장

  • 로그인한 사용자 정보를 ThreadLocal에 저장하고 컨트롤러에서 꺼내 쓰기

🔥 실전 예제: 로그인 사용자 정보 관리

👉 로그인한 유저 정보를 ThreadLocal로 저장해서 어디서든 꺼내 쓰기!

public class UserContext {
    private static final ThreadLocal<String> userThreadLocal = new ThreadLocal<>();

    public static void setUser(String username) {
        userThreadLocal.set(username);
    }

    public static String getUser() {
        return userThreadLocal.get();
    }

    public static void clear() {
        userThreadLocal.remove();  // 메모리 누수 방지
    }
}

// 서블릿 필터에서 로그인 정보 저장
public class AuthFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            String username = ((HttpServletRequest) request).getHeader("X-User");
            UserContext.setUser(username);  // ThreadLocal에 저장

            chain.doFilter(request, response);
        } finally {
            UserContext.clear();  // 요청 처리 후 정리 (안 하면 메모리 누수 위험!)
        }
    }
}

// 컨트롤러에서 사용자 정보 활용
public class UserController {
    public void handleRequest() {
        System.out.println("현재 사용자: " + UserContext.getUser());
    }
}

✅ 요청이 들어오면 AuthFilter에서 ThreadLocal에 사용자 정보 저장
✅ 컨트롤러에서 UserContext.getUser() 로 간편하게 가져다 씀
✅ 요청이 끝나면 clear() 로 메모리 누수 방지!


🚨 ThreadLocal 주의사항! (이거 안 지키면 큰일남!)

❌ 1. ThreadLocal은 꼭 remove() 해라!
→ clear() 안 하면 메모리 누수 발생 (특히 톰캣 같은 웹 서버에서 치명적)

❌ 2. 멀티스레드 환경에서 전역변수처럼 사용하면 안 됨
→ ThreadLocal은 스레드마다 다른 값을 저장하지만,
잘못 사용하면 데이터 꼬임 발생

❌ 3. ThreadPool을 사용할 때 조심하라!
→ 스레드가 재사용될 경우, 이전 값이 남아 있을 수 있음
(이럴 땐 remove() 필수!)


🎯 ThreadLocal 한 줄 정리!

✅ 각 스레드마다 독립적인 변수를 저장하는 기능
✅ 멀티스레드 환경에서 안전하게 데이터 관리 가능
✅ 로그인 정보, 트랜잭션 ID, 세션 정보 저장에 유용
✅ 사용 후 꼭 remove() 해서 메모리 누수 방지!

 

이제 ThreadLocal 개념 확실히 잡으셨죠? 😉
혹시 더 궁금한 점 있으면 댓글 남겨주세요! 💬✨

반응형