ThreadLocal에 대해
ThreadLocal은 무엇일까?
코드로 먼저 확인해보자
public class Shared {
private String nonDuplicatableId;
public void responseYourId() {
allocate();
System.out.println(nonDuplicatableId);
}
private void allocate() {
if (nonDuplicatableId == null) {
nonDuplicatableId = UUID.randomUUID().toString().substring(0, 6);
}
}
}
class SharedTest {
private static final Shared shared = new Shared();
private static final int COUNT = 2;
@Test
public void sharedTest() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(COUNT);
ExecutorService executorService = Executors.newFixedThreadPool(COUNT);
for (int i = 0; i < COUNT; i++) {
executorService.execute(shared::responseYourId);
latch.countDown();
}
latch.await();
}
}
위 테스트코드의 결과는 어떻게 나올까?Shared shared = new Shraed();
로 선언을 해두고 2개의 쓰레드에서 responseYourId
를 호출했다.allocate()
메서드 내부로직이 null일 때에 String nonDuplicatableId
에 값을 할당하기 때문에
똑같은 필드 변수가 print 될 것이다.
여러 쓰레드에서 접근하는 클래스에 상태를 두는 경우에는 동시성 문제를 항상 염두해서 코드를 작성해야한다.
가령, int count = 0
변수가 존재하는데 여러 쓰레드에서 이 값을 변경하고 return 받는 경우에 문제가 발생할 것이다.
위 코드의 예시와는 조금 동떨어지는 얘기긴하지만 어쨋든 동시성 이슈를 고려해야 한다.
그런데, 위 코드에서 각 쓰레드마다 각자 본인만의 값을 갖고 싶다면 어떻게 해야할까??
쉽게 말해서, 각 쓰레드마다 nonDuplicatableId
의 값이 달랐으면 좋겠다는 것이다.
하지만 단순히 String 필드로 운용하게 되면 동시성 문제를 마주하게 될 것이다.
이때 사용하는 것이 바로 ThreadLocal
이다. 쓰레드별로 지역 변수를 할당하게끔 해준다.
코드를 변경해보겠다.
public class Shared {
private final ThreadLocal<String> nonDuplicatableId = new ThreadLocal<>();
public void responseYourId() {
allocate();
System.out.println(nonDuplicatableId.get());
}
private void allocate() {
if (nonDuplicatableId.get() == null) {
nonDuplicatableId.set(UUID.randomUUID().toString().substring(0, 6));
}
}
}
위와 같이 변경했다. ThreadLocal<T>
의 사용법은 간단하다.new ThreadLocal<>();
을 선언하고 get()
을 이용해 값을 꺼내오고, set()
을 이용해 값을 넣고, remove()
를 이용해 제거한다.
유의해야 할 점은 사용을 다 한 후에는 무조건 remove()
를 이용해서 제거해주어야 한다는 것이다.
그 이유는 쓰레드 별로 값을 할당하기 때문에 쓰레드 풀을 사용할 시에 요청 사용자가 변경되어도 동일 쓰레드를 이용하면서 이전에 저장된 값을 사용하게 되는 경우가 발생하기 때문이다.
3줄 요약
- 쓰레드 별로 변수를 관리하고 싶을 때에
ThreadLocal
을 사용 get()
,set()
,remove()
를 이용해서 사용remove()
를 호출하지 않으면 쓰레드 풀을 사용할 때에, 동일한 쓰레드를 이용하는 별개의 사용자한테서 문제가 발생할 수 있음
REFERENCE
'Java' 카테고리의 다른 글
[Kotlin] 기본 생성자가 없고, 필드가 1개여도 코틀린은 역직렬화가 된다 (0) | 2023.05.12 |
---|---|
[Java] AttributeConverter를 이용하여 DB에 Entity의 컬렉션 필드 저장하기 (0) | 2022.08.02 |
[Java] Atomic, Volatile, Synchronized 에 대해 (2) | 2021.09.20 |
[Java] Reflection 사용법 정리 (0) | 2021.09.08 |
[Java] ExecutorService 와 ThreadPoolExecutor 에 대해 (0) | 2021.09.06 |