일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- php
- java
- jsp
- Web Server
- Oracle
- tool
- Spring
- IntelliJ
- Gradle
- redis
- it
- ubuntu
- Spring Batch
- JVM
- linux
- MySQL
- laravel
- springboot
- devops
- ReactJS
- 요리
- jenkins
- javascript
- Git
- Spring Boot
- AWS
- Design Patterns
- 맛집
- elasticsearch
- db
Archives
- Today
- Total
아무거나
Java ThreadLocal 본문
반응형
Java ThreadLocal
Java ThreadLocal 이란
Spring Bean 은 싱글톤으로 등록이 된다. 이러한 상황에서 하나의 인스턴스에 여러개의 스레드가 동시에 접근하면 동시성 문제가 발생하게 된다.
이러한 경우 스레드 세이프하게 하려면 ThreadLocal 을 사용하면 된다. ThreadLocal 은 해당 Thread 만 접근할 수 있는 특별한 저장소를 말한다. 즉, 각 Thread 마다 별도의 내부 저장소를 제공
동시성 문제 예제
하기 코드를 실행해보면 동시성 문제가 발생한다.
[ThreadLocalServiceTest.java]
package com.example.bkjeon.base.services.api.v1.thread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class ThreadLocalServiceTest {
private String nameStore;
// 매개변수를 통해 넘어온 name을 전역 변수인 nameStore에 저장 후, 1초 뒤 조회 로그를 찍어주는 메소드
public String logic(String name) {
log.info("저장 name={} -> nameStore={}", name, nameStore);
nameStore = name;
sleep(1000);
log.info("조회 nameStore={}",nameStore);
return nameStore;
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[ThreadController.java]
...
private final ThreadLocalServiceTest threadLocalServiceTest;
@ApiOperation("ThreadLocal 테스트(동시성 문제 발생 테스트)")
@GetMapping("/isThreadLocalTest")
public void isThreadLocal() {
log.info("main start");
Runnable userA = () -> {
threadLocalServiceTest.logic("userA");
};
Runnable userB = () -> {
threadLocalServiceTest.logic("userB");
};
Thread threadA = new Thread(userA);
threadA.setName("thread-A");
Thread threadB = new Thread(userB);
threadB.setName("thread-B");
threadA.start(); //A실행
sleep(2000); //동시성 문제 발생X
// sleep(100); //동시성 문제 발생O
threadB.start(); //B실행
sleep(3000); //쓰레드B가 끝날 때까지 메인 쓰레드 종료 대기
log.info("main exit");
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- Thread 2개를 생성 후 2초 간격으로 두 스레드 실행
- 동시성 문제 발생 X
저장 name=userA -> nameStore=userB 조회 nameStore=userA 저장 name=userB -> nameStore=userA 조회 nameStore=userB
- 동시성 문제 발생 X
- Thread 2개를 생성 후 0.1초 간격 두 스레드 실행
- 동시성 문제 발생 O
저장 name=userA -> nameStore=null 저장 name=userB -> nameStore=userA 조회 nameStore=userB 조회 nameStore=userB
- 동시성 문제가 발생하는 이유
- 스프링 빈은 싱글톤으로 생성되기 때문에 인스턴스 1개를 공유해서 함께 사용한다.
- 스레드A가 nameStore 에 저장 후 조회를 하는데는 1초의 지연시간이 있다. 그 지연시간동안 스레드B가 nameStore 에 접근해서 값을 바꿔버리게 되면 스레드A는 변경된 값을 얻게 되므로 문제가 생긴다.
- 동시성 문제 발생 O
ThreadLocal 사용
상기 동시성 문제가 발생하였던 코드를 수정해보자.
[ThreadLocalService.java]
package com.example.bkjeon.base.services.api.v1.thread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
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();
}
}
}
주의할점
ThreadLocal 은 ThreadLocal Pool 환경에서 사용시에 반드시 remove() 를 호출하여 관리해야 한다.
그렇지 않으면 해당 스레드를 부여받게 되는 다른 사용자가 기존에 세팅된 ThreadLocal 의 데이터를 공유하게 될 수도 있기 때문이다.
참고
- https://velog.io/@gmtmoney2357/
- https://velog.io/@wken5577/
반응형
'Java & Kotlin > Java' 카테고리의 다른 글
Java Fork Join Pool 과 비동기 프로그래밍 (0) | 2024.11.08 |
---|---|
[Spring Actuator] Example 2편 - 각 Info Endpoint 설명 및 Custom Info Endpoint 생성 설명 (0) | 2023.11.26 |
Redis 성능 개선을 위한 GZIP 압축 로직을 적용시 Native OOM 이슈에 대한 분석 (0) | 2023.07.26 |
JDK8 에서 Perm 영역 삭제와 Metaspace 영역의 추가 (0) | 2023.07.17 |
JVM Heap 모니터링 및 Heap Dump 분석 (0) | 2023.03.09 |
Comments