변수를 공유하는 방법
객체는 Heap 또는 Stack 메모리 영역에 배치시킬 수 있습니다. Heap 영역은 일반적으로 모든 thread에서 접근 할 수 있으며 stack은 thread 하나당 만들어 지는 메모리 영역으로 thread간 접근이 불가능한 것으로 알려져 있습니다.
아래 코드의 UserRepository 변수는 Heap 영역에 만들어진 객체를 가리키고 있으며 다른 곳에서도 해당 객체를 바로 접근할 수 있습니다. 함께 공유해서 사용하기 때문에 여러 thread에서 사용할 때 공유된 정보로써 제공할 수 있습니다. 따라서 만약 UserRepository가 설정 정보를 가지고 있고 이를 변경한다면 사용하고 있는 모든 곳에서 영향을 받게 됩니다.
ThreadLocal 이란
ThreadLocal을 정의하기전에 변수의 선언에 대해서 메모리 관점으로 알아보았습니다. 그 이유는 바로 ThreadLocal은 한 thread 안에서 파라미터 또는 리턴 값으로 정보를 제공하는 것이 아닌 다른 방법으로 thread 안에서의 값을 공유하는 방법을 제공해주기 때문입니다. 이렇게 Stack 영역에 변수를 선언하는 것에 대해서 단점을 해소해줄 수 있습니다.
ThreadLocal의 내부는 thread 정보를 key로 하여 값을 저장해두는 Map 구조를 가지고 있습니다. 기본적인 사용에는 get, set 메서드를 이용합니다. 아래의 코드를 통해 한번 사용해보도록 하겠습니다.
기본 사용 ( 선언, get, set )
public class UserTermService {
public static ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>(); // ThreadLocal 사용을 위한 선언
public UserTermService(Integer value) {
threadLocalValue.set(value); // ThreadLocal에 set 값을 세팅
}
public void threadLocal_1() {
System.out.println("threadLocalValue : " + threadLocalValue.get()); // ThreadLocal에 있는 값을 가져오기
}
}
테스트 코드를 아래와 같이 작성하였고 예상하는 값은 threadLocalValue : 5 이며 정상적으로 호출 된 것을 확인할 수 있었습니다.
public class ThreadLocalTest {
@Test
public void threadLocalTest() {
UserTermService userTermService = new UserTermService(5);
userTermService.threadLocal_1(); // threadLocalValue : 5 출력 확인
}
}
추가적인 메서드
추가적으로 ThreadLocal은 아래처럼 withInitial 메서드를 이용하면 lambda funciton을 파라미터로하여 기본값을 설정해 줄 수 있습니다.
public static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 5);
또한 remove 메서드를 이용하여 설정된 값을 삭제할 수 있습니다.
threadLocalValue.remove();
주의사항
ThreadLocal을 사용할 때 반드시 명심해야 할 부분이 있습니다. ThreadLocal은 Thead의 정보를 key로 하여 Map의 형식으로 데이터를 저장한 후 사용할 수 있는 자료구조를 가지고 있습니다. 따라서 만약 ThreadPool을 사용하여 thread를 재활용한다면 동일한 이전에 세팅했던 ThreadLocal의 정보가 남아있어 원치않는 동작을 할 수 있습니다. 따라서 ThreadPool을 사용하는 경우에는 반드시 모두 사용 후 THreadLocal의 값을 remove 메서드를 사용하여 값을 제거해주는것이 필요합니다.
활용
그렇다면 이러한 ThreadLocal은 어디서 사용하는 걸까요 ? 이는 Spring Security에서 사용자 인증 정보를 사용할 때 확인할 수 있었습니다. Spring Security를 사용하여 사용자 인증정보를 가져올 때 아래 코드를 통해서 가져올 수 있습니다. 해당 코드를 이용하면 방금 요청된 정보의 인증정보를 내가 만들고 있는 비즈니스 코드에서 이용할 수 있는 것입니다.
SecurityContextHolder.getContext().getAuthentication();
이 코드를 내부로 들어가면 아래와 같은 코드를 발견할 수 있었습니다. getContext() 메서드 내부가 ThreadLocal을 통해 구현되어 있었으며 Thread 별로 인증정보를 다르게 가지고 있는 것을 알 수 있었습니다.
final class ThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
// ~ Methods
// ========================================================================================================
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
'
'Study > Java' 카테고리의 다른 글
Java에서 System.getProperty() 사용법 / 현재 디렉토리 알기 / user home 디렉토리 알기 (0) | 2024.02.06 |
---|---|
Java Enum 1편 : Enum 기본적인 사용 (0) | 2024.02.05 |
[Java] 객체지향 프로그래밍 (OOP) (2) | 2024.01.21 |
Java에서 일정 시간 후에 실행을 중지하는 방법 (1) | 2023.12.04 |
[Java/자바] java.lang.Integer cannot be cast to java.lang.String 해결 방법 (0) | 2023.06.19 |
댓글