ThreadLocal과 주의 사항

Choizz·2023년 1월 25일
0

싱글톤에서의 필드 공유 문제와 Spring Security에서 동시성 문제를 해결하기 위해 ThreadLocal을 사용한다는 것을 알 수 있었다.

그래서 이번엔 ThreadLocal에 관하여 포스팅 해보려고 한다.


ThreadLocal

  • ThreadLocal 클래스는 ThreadLocal 변수를 제공하는데, 이 변수가 스레드에 할당되면 할당된 스레드만 변수에 접근할 수 있다.
  • 즉, ThreadLocal은 해당 스레드만 접근할 수 있게 하는 저장소같은 역할을 한다.

  1. user A에 thread1 할당 -> thread1에서 apple를 쓰레드 로컬에 저장.
  2. user B에 thread2 할당 -> thread2에서 orange를 쓰레드 로컬에 저장.
  3. 각각 스레드만이 저장된 데이터에 접근 가능함. 즉, thread2는 apple에 접근할 수 없음.

ThreadLocal에 데이터를 저장하면 할당된 스레드만이 데이터에 접근할 수 있는 것이다. 따라서 동시성으로 인한 문제를 방지할 수 있다.

동시성으로 인한 문제에 관련한 코드는 싱글톤 패턴에서 필드 공유 문제 를 참고 바란다.


주의할 점

  • 쓰레드 풀에서 ThreadLocal을 사용하는 경우 주의할 점이 있다.
    쓰레드 풀을 사용하는 경우 쓰레드가 자신의 일이 끝나면 다시 쓰레드 풀로 돌아온다.
    이 때, ThreadLocal의 데이터를 삭제하지 않고 계속 유지하고 있다면다음에 같은 쓰레드를 하는 다른 사용자가 이 데이터를 조회할 수 있게 된다.
    따라서, 쓰레드가 반납될 때, 쓰레드 로컬의 데이터를 제거해 주어야 한다.

테스트

  • /user URL로 GET 요청을 하면 ThreadLocal에 "apple"이 저장하고, "apple"을 저장한 쓰레드와 ThreadLocal에 저장된 데이터를 response로 리턴한다.
  • 하지만 쓰레드가 임무를 수행하고 반납될 때, ThreadLocal을 지우지 않았다.
  • 이 경우, 다른 유저가 쓰레드 풀에서 같은 쓰레드를 사용했을 때, 데이터가 남아있는지 확인해보자!

코드

@Slf4j  
@RestController  
public class Controller {  
   //ThreadLocal 객체를 만든다.
   ThreadLocal<String> favoriteFood = new ThreadLocal<>();  
   
   //이 URL로 GET 요청시 ThreadLocal에 데이터를 저장한다.
   @GetMapping("/user")  
   public String request() {  
      favoriteFood.set("apple"); //ThreadLocal에 데이터를 저장한다.
      return Thread.currentThread().getName() + " "+ favoriteFood.get();//ThreadLocal에 저장된 데이터를 가지고 온다.  
   }  

   // ThreadLocal에 데이터가 저장되어 있는 지 확인한다.
   @GetMapping  
   public String response() {  
      if (favoriteFood.get() == null) {  
         return Thread.currentThread().getName() + " is Null";  
      }  
      return Thread.currentThread().getName() + favoriteFood.get();  
   }  
  
}
  • http://localhost:8080/user 로 요청을 보냈을 때, 할당된 쓰레드와 그 쓰레드에 ThreadLocal에 저장된 데이터를 보여준다. ThreadLocal에 데이터는 삭제하지 않았다.
  • 이제 다른 유저들이 같은 쓰레드로 요청을 했을 때를 확인해 보자.(http://localhost:8080)
    - jmeter를 이용해서 10명의 유저가 동시에 요청을 보냈다고 가정했다.
  • ThreadLocal에 데이터를 저장한 쓰레드와 같은 쓰레드( http-nio-8080-exec-1)를 사용한 유저들은 처음에 ThreadLocal에 저장됐던 apple을 그대로 조회할 수 있었다.

  • 따라서, 데이터를 공유하지 않게 하기 위해 마지막에 remove() 메서드들 통해 ThreadLocal을 제거해야 한다.

정리

  • 동시성 문제를 해결 방법으로 ThreadLocal을 사용할 수는 있지만, 쓰레드 풀 사용 환경에서는 쓰레드가 작업을 모두 마친 후 반납되기 전에 ThreadLocal을 제거해 주어야 한다.

Reference

profile
집중

0개의 댓글