일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- StringBuffer
- 현금흐름표
- mco
- 무디스
- 알고리즘
- javascript
- 기업분석
- 인플레이션
- 미국주식
- etf
- 백준
- 잉여현금흐름
- S&P500
- 프로그래머스
- object
- 객체지향
- XLF
- 배당성장
- 제태크
- 그리디 알고리즘
- 접근제어자
- 주식
- 금리인하
- 다형성
- 오버라이딩
- Java
- FCF
- 자바
- 주린이
- 금리인상
- Today
- Total
오늘의하루
[Spring] Thread Local 사용 시 주의 사항 본문
Java에서 기본적으로 제공하는 ThreadLocal은 동시성 문제를 해결하는 가장 기본적인 방법 중 하나입니다.
- ThreadLocal은 내부적으로 Map 자료 구조를 통해 각 Thread 별로 데이터를 저장하고 불러오기 때문에 각 Thread는 자신만의 데이터를 독립적으로 가질 수 있습니다.
Thread와 ThreadPool
Thread는 하나의 프로세스 내에서 실행 단위를 뜻합니다.
Spring을 통해 웹 개발을 할 때 일반적으로 ThreadPool을 통해 Thread를 관리합니다.
이러한 상황에서 ThreadLocal을 사용할 경우, 몇 가지 주의해야 할 점이 있습니다.
주의사항 1. ThreadPool 재사용 문제
ThreadPool에서는 Thread가 응답 후 사라지는 것이 아니고 다시 ThreadPool로 반환됩니다.
예를 들어 사용자 A가 요청을 보낼 때 ThreadPool에서 Thread-A가 할당되어 작업을 수행하고 ThreadLocal을 통해 특정 값을 저장했다고 가정해 봅시다.
이 저장된 값은 오직 Thread-A에서만 조회 및 변경이 가능합니다.
그러나 작업이 완료된 후 Thread-A는 ThreadPool로 반환되고 ThreadLocal에 저장된 값이 제거되지 않은 상태로 남아 있습니다.
다음 사용자 B가 요청을 보낼 때 ThreadPool에서 다시 Thread-A가 할당된다면 사용자 B는 사용자 A가 ThreadLocal에 저장한 값을 보게 되는 문제가 발생할 수 있습니다.
또한 경우에 따라 사용자가 사용하지 않는 값이 ThreadLocal 객체에 저장되므로 메모리 누수 문제가 발생할 수 있습니다.
예제) ThreadLocal의 값을 제거하지 않은 경우
@Slf4j
public class ThreadLocalRemoveTest {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
private String[] str = {"A", "B", "C", "D"};
@Test
void threadLocalRemoveX() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
AtomicInteger index = new AtomicInteger(0);
for(int i = 0; i < 4; i++) {
int currentIndex = index.getAndIncrement();
executorService.submit(() -> {
if(threadLocal.get() == null) {
threadLocal.set(str[currentIndex]);
}
log.info("[{}] {}", Thread.currentThread().getName(), threadLocal.get());
});
}
executorService.shutdown();
}
}
출력) ThreadLocal의 값을 제거하지 않은 경우
[pool-2-thread-1] A
[pool-2-thread-2] B
[pool-2-thread-1] A
[pool-2-thread-2] B
이러한 문제를 방지하기 위해서 Thread 작업이 끝난 후 remove()를 통해 ThreadLocal 값을 명시적으로 제거해야 합니다.
예제) ThreadLocal의 값을 제거한 경우
@Slf4j
public class ThreadLocalRemoveTest {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
private String[] str = {"A", "B", "C", "D"};
@Test
void threadLocalRemoveO() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
AtomicInteger index = new AtomicInteger(0);
for(int i = 0; i < 4; i++) {
int currentIndex = index.getAndIncrement();
executorService.submit(() -> {
if(threadLocal.get() == null) {
threadLocal.set(str[currentIndex]);
}
log.info("[{}] {}", Thread.currentThread().getName(), threadLocal.get());
threadLocal.remove(); // 사용 완료 후 ThreadLocal에 저장된 값 제거
});
}
executorService.shutdown();
}
}
출력) ThreadLocal의 값을 제거한 경우
[pool-1-thread-1] A
[pool-1-thread-2] B
[pool-1-thread-1] C
[pool-1-thread-2] D
주의사항 2. 메모리 사용 문제
만약 ThreadPool을 사용하지 않고 매번 새로운 Thread를 생성하는 환경에서 ThreadLocal을 사용하게 되면 많은 양의 메모리를 소비하게 됩니다.
ThreadLocal에는 각 Thread마다 저장되기 때문에 Thread가 많아질수록 메모리 사용량이 급증하게 됩니다.
이는 곧 성능 저하 문제를 유발할 수 있습니다.
결론
ThreadLocal을 사용할 때는 반드시 사용이 끝난 후 remove() 메서드를 호출하여 데이터를 명시적으로 제거해야 합니다.
그렇지 않으면 메모리 누수와 사용자 데이터의 불필요한 공유 문제가 발생할 수 있습니다.
이를 통해 스레드 풀의 재사용으로 인한 데이터 누출과 메모리 낭비를 방지할 수 있습니다.
'Spring' 카테고리의 다른 글
BeanPostProcessor 사용법: 빈 초기화 과정에서의 조작과 시점 이해하기 (0) | 2024.11.07 |
---|---|
[Spring] Proxy 내부 호출 문제 해결 및 순환 참조 문제 해결 (1) | 2024.08.28 |
[Spring] 부하 테스트에서 발견한 회원가입 로직 이상 동작에 대한 고찰 (0) | 2024.05.16 |
Spring 서비스 추상화와 단일 책임 원칙 (0) | 2023.10.16 |
Spring boot 오류 페이지 (0) | 2023.08.30 |