java / / 2023. 10. 11. 06:37

ThreadLocal 사용법

아래의 경우처럼 두 개의 Thread가 동시에 공유 변수에 값을 저장하고 조회하는 경우가 있다. 시간의 차이로 인해 값의 내용이 원하는 결과로 조회되지 않는다.

아래의 테스트 케이스를 한번 보자.

public class ThreadLocalTest {

    @Test
    public void test() throws InterruptedException {
        User user = new User();

        Thread thread1 = new Thread(() -> {
            user.setName("hong_1");
            sleep(1000);
            System.out.println("thread1 user: " + user.getName());
        });

        Thread thread2 = new Thread(() -> {
            sleep(500);
            user.setName("hong_2");
        });

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println(user.getName());

    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Getter
    @Setter
    public static class User {
        private String name;
    }
}
  • 공유 객체(User)를 사용하여 2개의 Thread에서 사용자를 동시에 생성한다.
  • Thread 1에서 이름을 세팅하고 1초간 대기하고 다시 조회한다.
  • 생성한 사용자의 이름을 조회할 때 1번 Thread에서 만든 이름(hong_1)을 다시 조회하면 hong_2로 출력이 되는 문제가 있다.

ThreadLocal

이 문제는 여러 Thread에서 공유 변수를 사용할 때 발생하는 문제이다. 이러한 문제를 해결하고자 Thread 별로 로컬 변수를 사용할 수가 있는데 그것이 ThreadLocal이다.

소스 예제를 한번 보자.

@Getter
public static class UserThreadLocal {
    private ThreadLocal<String> nameStore = new ThreadLocal<>();

    public void setName(String name) {
        this.nameStore.set(name);
    }

    public String getName() {
        return this.nameStore.get();
    }
}

기존에 String name으로 저장한 값을 ThreadLocal<String> nameStore로 저장을 한다.

gettersetter를 ThreadLocal의 getset을 사용한다.

그리고 테스트 케이스를 작성해보자.

@Test
public void testWithThreadLocal() throws InterruptedException {
    UserThreadLocal userThreadLocal = new UserThreadLocal();

    Thread thread1 = new Thread(() -> {
        userThreadLocal.setName("hong_1");
        sleep(1000);
        System.out.println("thread1 user: " + userThreadLocal.getName());
        userThreadLocal.getNameStore().remove();
    });

    Thread thread2 = new Thread(() -> {
        sleep(500);
        userThreadLocal.setName("hong_2");
        userThreadLocal.getNameStore().remove();
    });

    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();
}

결과는 예상한 대로 hong_1으로 잘 나오는 것을 확인할 수 있다.

주의사항

ThreadLocal은 사용을 하고 나면 반드시 remove()를 통해 제거를 해줘야 한다. 그렇지 않으면 ThreadPool을 사용하는 환경에서는 원하지 않는 결과를 가져올 수 있다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유