동시성 TreadLocal

떡ol·2023년 11월 29일

1.동시성 문제

다음과 같은 코드가 있다고 가정하자.

        log.info("main start");
        Runnable userA = () -> {
            service.logic("userA");
        };
        Runnable userB = () -> {
            service.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();

위 코드는 Thread를 두개 생성하여 0.1초대기후에 실행하는 예제이다. 원하는 결과는 A,B 각각 다른 값의 Name이 들어가는 것을 원하겠지만 결과는 그렇지 않다.

[Test worker] main start
[Thread-A] 저장 name=userA -> nameStore=null
[Thread-B] 저장 name=userB -> nameStore=userA
[Thread-A] 조회 nameStore=userB
[Thread-B] 조회 nameStore=userB
[Test worker] main exi

userB의 결과가 두번 찍혔다.

이처럼 여러 쓰레드가 동시에 같은 인스턴스의 필드 값을 변경하면서 발생하는 문제를 동시성 문제라 한다. 이런 동시성 문제는 여러 쓰레드가 같은 인스턴스의 필드에 접근해야 하기 때문에 트래픽이 적은 상황에서는 확률상 잘 나타나지 않고, 트래픽이 점점 많아질 수 록 자주 발생한다.
이런 동시성 문제는 지역 변수에서는 발생하지 않는다. 지역 변수는 쓰레드마다 각각 다른 메모리 영역이 할당된다.

2. ThreadLocal

바로 해결 방법을 알아보자. ThreadLocal을 이용하여 고유한 값을 setter에 넣어주면 된다.

public class ThreadLocalService {

   private ThreadLocal<String> nameStore = new ThreadLocal<>(); // String 타입으로 만들어준다.
   
   public String logic(String name) {
       log.info("저장 name={} -> nameStore={}", name, nameStore.get());
       nameStore.set(name); // 그저 set을 이용해서 name을 설정해주면 된다.
       sleep(1000);
       log.info("조회 nameStore={}",nameStore.get()); // 사용할때는 get을 사용해서 불러온다.
       return nameStore.get();
   }
   
   private void sleep(int millis) {
       try {
       		Thread.sleep(millis);
       } catch (InterruptedException e) {
       		e.printStackTrace();
       }
   }
}

사용법은 위에 코드에 나와있든 너무쉽다. setget을 이용해서 저장 및 불러와서 해당 인스턴트를 사용하면 된다.

ThreadLocal 사용법

값 저장: ThreadLocal.set(xxx)
값 조회: ThreadLocal.get()
값 제거: ThreadLocal.remove()

사용한 쓰레드는 remove()를하여 메모리에서 날려주는 것을 잊지말자. 메모리 누수의 원인이 된다.

profile
하이

0개의 댓글