SPRING/스프링

[스프링 핵심 원리-고급] ThreadLocal

노트코드 2022. 1. 21. 01:06

쓰레드 로컬해당 쓰레드만 접근할 수 있는 특별한 저장소를 말한다.

(같은 인스턴스의 쓰레드 로컬 필드에 접근해도 문제가 없음)

  • ThreadLocal을 활용한 예제 코드 작성
@Slf4j
public class ThreadLocalService {

    private ThreadLocal<String> nameStore = new ThreadLocal<>();

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

        sleep(1000);
        log.info("조회 nameStore={}", nameStore.get());
        return nameStore.get();
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

    }
}
  • 동시성 문제가 발생하지 않는 코드 작성
@Slf4j
public class ThreadLocalServiceTest {

    private ThreadLocalService fieldService = new ThreadLocalService();

    @Test
    void field() {
        log.info("main start");
        Runnable userA = () -> fieldService.logic("userA");
        Runnable userB = () -> fieldService.logic("userB");

        Thread threadA = new Thread(userA);
        threadA.setName("thread-A");

        Thread threadB = new Thread(userB);
        threadB.setName("thread-B");

        threadA.start();
        sleep(2000);
        threadB.start();
        sleep(3000);
        log.info("main exit");
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

//01:00:39.413 [Test worker] INFO hello.advanced.threadlocal.ThreadLocalServiceTest - main start
//01:00:39.419 [thread-A] INFO hello.advanced.threadlocal.code.ThreadLocalService - 저장 name=userA -> nameStore=null
//01:00:40.439 [thread-A] INFO hello.advanced.threadlocal.code.ThreadLocalService - 조회 nameStore=userA
//01:00:41.424 [thread-B] INFO hello.advanced.threadlocal.code.ThreadLocalService - 저장 name=userB -> nameStore=null
//01:00:42.431 [thread-B] INFO hello.advanced.threadlocal.code.ThreadLocalService - 조회 nameStore=userB
//01:00:44.435 [Test worker] INFO hello.advanced.threadlocal.ThreadLocalServiceTest - main exit
}​

 

 

  1. userA요청이 들어옴 nameStore에 userA저장
  2. userB요청이 들어옴 nameStore에 null인 상태를 볼수 있음(기존 이전글에서는 userA가 저장되어있었음)
  • 동시성 발생하게 코드 작성
@Test
    void field() {
        log.info("main start");
        Runnable userA = () -> fieldService.logic("userA");
        Runnable userB = () -> fieldService.logic("userB");

        Thread threadA = new Thread(userA);
        threadA.setName("thread-A");

        Thread threadB = new Thread(userB);
        threadB.setName("thread-B");

        threadA.start();
        sleep(100);
        threadB.start();
        sleep(3000);
        log.info("main exit");
        
//01:03:57.595 [thread-A] INFO hello.advanced.threadlocal.code.ThreadLocalService - 저장 name=userA -> nameStore=null
//01:03:57.711 [thread-B] INFO hello.advanced.threadlocal.code.ThreadLocalService - 저장 name=userB -> nameStore=null
//01:03:58.616 [thread-A] INFO hello.advanced.threadlocal.code.ThreadLocalService - 조회 nameStore=userA
//01:03:58.722 [thread-B] INFO hello.advanced.threadlocal.code.ThreadLocalService - 조회 nameStore=userB
    }
  1. userA요청이 들어오고 nameStore에 userA저장중
  2. userA요청이 끝나기전에 userB요청이 들어왔지만, ThreadLocal로 인해 서로의 값에 영향을 주지 않음