티스토리 뷰

ThreadLocalInheritableThreadLocal에 대해 많이 들어보신 분도 계시겠지만 생소하신 분도 계실 것 같습니다.

두 클래스는 Java 1.2에 등장한 클래스이며 ThreadLocalInheritableThreadLocal대해 한번 알아보겠습니다.

 

ThreadLocal

쓰레드 로컬 변수를 제공하며 이 로컬 변수는 get 또는 set 메서드를 통해 액세스 하거나 변경할 수 있으며 쓰레드 마다 독립적으로 갖고있는 변수를 말합니다.

ThreadLocal의 설명을 해석하자면 위와 같습니다. 즉, ThreadLocal에 할당된 변수는 각 쓰레드마다 고유하게 할당되므로 멀티쓰레드 간 쓰레드 세이프 하게 프로그램을 작성할 수 있습니다. 예제코드를 함께 살펴보겠습니다.

 

ThreadLocal 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.demo;
 
public class DemoApplication {
    private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();
 
    public static void main(String[] args) {
        THREAD_LOCAL.set("hello");
        System.out.println("Thread : " + Thread.currentThread());
        System.out.println("ThreadLocal Variable : " + THREAD_LOCAL.get());
 
        Thread thread = new Thread(() -> {
            System.out.println("Thread : " + Thread.currentThread());
            System.out.println("ThreadLocal Variable : " + THREAD_LOCAL.get());
        });
        thread.start();
    }
}
cs

Thread 인스턴스의 정보와 ThreadLocal에 저장된 값을 출력하는 간단한 코드입니다.

하나는 main 메서드에서 실행되고 또 하나는 별도의 Thread 인스턴스를 만든 후 그 내부에서 출력하는 코드입니다.

 

 

main 쓰레드에서 ThreadLocal 인스턴스에 "hello"라는 데이터를 저장 후 main 쓰레드에서 ThreadLocal 인스턴스에 저장된 값을 꺼내오면 정상적으로 반환되지만, main 쓰레드에서 생성된 또 하나의 쓰레드에서는 값이 null로 출력되는 걸 확인하실 수 있습니다.

즉, "hello" 라는 데이터는 main 쓰레드 외에 다른 쓰레드에서는 접근할 수 없습니다.

 

 

InheritableThreadLocal

ThreadLocal을 확장한 클래스로 상위 쓰레드에서 하위 쓰레드로의 값의 상속을 제공하여 하위 쓰레드가 생성되면 상위 쓰레드에 저장된 값을 상속받아서 사용할 수 있습니다. 일반적으로 하위 쓰레드의 값은 상위 쓰레드와 동일하지만 childValue 메서드를 재정의하여 상위 쓰레드의 값이 아닌 임의의 값을 반환하게끔 할 수 있습니다.

InheritableThreadLocal의 설명을 해석하자면 위와 같습니다.

즉, 상위 쓰레드에 생성된 값을 하위 쓰레드에서 그대로 사용할 수 있고 childValue 메서드를 재정의하여 저장된 변수에 값을 얻어올 때 상위 쓰레드에 저장된 값이 아닌 임의의 값을 반환할 수 있습니다. 예제코드를 함께 살펴보겠습니다.

 

 

InheritableThreadLocal 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.demo;
 
public class DemoApplication {
    private static final ThreadLocal<String> INHERITABLE_THREAD_LOCAL = new InheritableThreadLocal<>();
 
    public static void main(String[] args) {
        INHERITABLE_THREAD_LOCAL.set("hello");
        System.out.println("Thread : " + Thread.currentThread());
        System.out.println("ThreadLocal Variable : " + INHERITABLE_THREAD_LOCAL.get());
 
        Thread thread = new Thread(() -> {
            System.out.println("Thread : " + Thread.currentThread());
            System.out.println("ThreadLocal Variable : " + INHERITABLE_THREAD_LOCAL.get());
        });
        thread.start();
    }
}
cs

위 코드와 로직은 동일하며 변수의 타입을 ThreadLocal이 아닌 InheritableThreadLocal로 변경하였습니다.

 

 

ThreadLocal과는 달리 InheritableThreadLocal에서는 main 쓰레드(상위 쓰레드)에 저장된 값을 하위 쓰레드에서도 정상적으로 값을 얻어온 걸 확인하실 수 있습니다.

즉, 상위 쓰레드에서 생성된 값을 하위 쓰레드에도 공유가 되는 걸 알 수 있습니다.

 

 

InheritableThreadLocal 예제 (2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.example.demo;
 
public class DemoApplication {
    public static void main(String[] args) {
        Parent parent = new Parent();
        parent.start();
    }
}
 
class Parent extends Thread {
    public static final ThreadLocal<String> INHERITABLE_THREAD_LOCAL = new InheritableThreadLocal<>() {
        @Override
        public String childValue(String parentValue) {
            return "child value";
        }
    };
 
    @Override
    public void run() {
        INHERITABLE_THREAD_LOCAL.set("parent");
 
        System.out.println("Parent Thread : " + Thread.currentThread());
        System.out.println("Parent ThreadLocal Variable : " + INHERITABLE_THREAD_LOCAL.get());
 
        Child child = new Child();
        child.start();
    }
}
 
class Child extends Thread {
    @Override
    public void run() {
        System.out.println("Child Thread : " + Thread.currentThread());
        System.out.println("Child ThreadLocal Variable : " + Parent.INHERITABLE_THREAD_LOCAL.get());
    }
}
cs

이번에는 childValue 메서드를 재정의하여 하위 쓰레드에서 값을 읽어올 때 상위 쓰레드에 있는 값이 아닌 임의의 값을 얻도록 작성해보겠습니다.

 

 

결과는 의도한 데로 출력되었습니다. 이번 글에서는 ThreadLocal과 InheritableThreadLocal에 대해 간단하게 알아보았습니다.  두 클래스의 특성을 잘 파악하여 애플리케이션 로직에 적절하게 녹여내면 좋을 것 같습니다.

 

 

ThreadLocal과 InheritableThreadLocal의 내부구조 간단히 보기

ThreadLocal과 InheritableThreadLocal은 내부적으로 ThreadLocalMap라는 클래스에 key, value로 관리됩니다.

key는 위 사진처럼 현재 실행되고 있는 쓰레드가 key값이 됩니다.

 

 

주의사항

예제코드에서는 쓰레드를 직접 생성하고 ThreadLocal에 값을 세팅하여 조회해오는 로직이 있었습니다.

하지만 실무에서는 Spring Framework를 이용해 웹 애플리케이션을 개발하실 텐데 이때 ThreadLocal에 저장된 값을 삭제해주지 않으면 의도치 않는 문제가 발생할 수 있습니다.

 

클라이언트가 요청을 보내면 쓰레드를 직접 생성하지 않고 미리 ThreadPool에 만들어둔 쓰레드를 가져다 쓰고 반납하기 때문에 이전에 처리되었던 쓰레드로부터 생성된 ThreadLocal에 저장된 값이 남아있을 수 있습니다.

 

그래서 ThreadLocal은 사용 후 바로 삭제해주어야 합니다.

 

 

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함