ThreadLocal 변수를 언제 어떻게 사용해야 합니까?
변수는 언제 사용해야 합니까?
어떻게 사용하나요?
스레드 세이프가 아닌 오브젝트가 있는 경우(SimpleDateFormat)에는 액세스 동기화를 피하고 싶은 경우가 있습니다(SimpleDateFormat 입니다.대신 각 스레드에 고유한 개체 인스턴스를 지정하십시오.
예를 들어 다음과 같습니다.
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
문서.
★★★★★★★★★★★★★★★★★★부터ThreadLocal
주어진 데이터 내의 참조입니다.Thread
, , , , , , , , , , , , , , , , , , , ,를 사용하면 할 수 ThreadLocal
는 스레드 풀을 사용하는 어플리케이션서버에 있습니다.할 .ThreadLocal
사용자 »get()
★★★★★★★★★★★★★★★★★」set()
를 ThreadLocal
의 »remove()
★★★★★★ 。
작업을 완료할 때 정리하지 않으면 배포된 웹 앱의 일부로 로드된 클래스에 대한 참조가 영구 힙에 남아 가비지가 수집되지 않습니다.웹 앱의 재도입/전개를 실시해도, 각 앱이 정리되는 것은 아닙니다.Thread
의 웹Thread
웹 앱 소유가 아닙니다.이후 전개할 때마다 가비지가 수집되지 않는 클래스의 새 인스턴스가 생성됩니다.
.java.lang.OutOfMemoryError: PermGen space
을 좀 한 후에 더 많이 하게 될 것이다.-XX:MaxPermSize
버그를 수정하는 대신.
이러한 문제가 발생하는 경우 Eclipse의 메모리 아나라이저를 사용하거나 Frank Kievet의 가이드와 후속 조치를 따라 참조가 유지되고 있는 스레드와 클래스를 판별할 수 있습니다.
업데이트: Alex Vasseur의 블로그 엔트리를 재검출했습니다.이 엔트리는 몇 가지 추적에 도움이 되었습니다.ThreadLocal
내가 겪고 있던 문제들
많은 프레임워크에서 ThreadLocals를 사용하여 현재 스레드와 관련된 일부 컨텍스트를 유지합니다.예를 들어 현재 트랜잭션이 ThreadLocal에 저장되어 있는 경우 스택 다운에 있는 누군가가 액세스할 필요가 있는 경우 모든 메서드 호출을 매개 변수로 전달할 필요가 없습니다.웹 응용 프로그램은 현재 요청 및 세션에 대한 정보를 ThreadLocal에 저장하여 응용 프로그램에 쉽게 액세스할 수 있습니다.Guice를 사용하면 주입된 객체에 대한 사용자 지정 범위를 구현할 때 ThreadLocals를 사용할 수 있습니다(Guice의 기본 서블릿 범위도 대부분 이 범위를 사용합니다).
ThreadLocals는 글로벌 변수의 일종입니다(단, 1개의 스레드로 제한되어 있기 때문에 약간 악성이 낮습니다).따라서 불필요한 부작용이나 메모리 누수를 방지하기 위해 사용할 때는 주의해야 합니다.ThreadLocal 값이 더 이상 필요하지 않을 때 항상 자동으로 지워지고 API를 잘못 사용할 수 없도록 API를 설계합니다(예를 들어 다음과 같습니다).ThreadLocals를 사용하여 코드를 보다 깔끔하게 만들 수 있습니다.또한 드물게는 ThreadLocals가 동작하는 유일한 방법이기도 합니다(현재 진행 중인 프로젝트에서는 2개의 케이스가 있습니다.이것들은 여기의 「Static Fields and Global Variables」에 기재되어 있습니다).
Java에서는 스레드마다 다를 수 있는 데이텀이 있는 경우 해당 데이텀을 필요한 모든 메서드에 전달하거나(또는 필요할 수 있음) 데이텀을 스레드에 관련짓는 방법을 선택할 수 있습니다.모든 메서드가 이미 공통의 "콘텍스트" 변수를 전달해야 하는 경우 모든 곳에 데이텀을 전달하는 것이 가능할 수 있습니다.
그렇지 않은 경우 추가 매개 변수를 사용하여 메서드 서명을 복잡하게 만들지 않을 수 있습니다.스레드화되지 않은 세계에서는 글로벌 변수와 동등한 Java를 사용하여 문제를 해결할 수 있습니다.스레드 워드에서 글로벌 변수와 동등한 변수는 스레드 로컬 변수입니다.
Java Concurrency in Practice 책에는 매우 좋은 예가 있습니다.여기서 저자(Joshua Bloch)는 스레드 구속이 스레드 구속을 유지하는 가장 간단한 방법 중 하나이며 스레드 로컬이 스레드 구속을 유지하는 보다 공식적인 방법 중 하나임을 설명합니다.마지막으로, 그는 사람들이 그것을 글로벌 변수로 사용함으로써 어떻게 그것을 남용할 수 있는지를 설명한다.
전술한 책에서 본문을 복사했습니다만, 스레드 로컬의 사용처를 이해하는 것은 그다지 중요하지 않기 때문에, 코드 3.10이 누락되어 있습니다.
스레드 로컬 변수는 종종 가변 싱글톤 또는 전역 변수를 기반으로 한 설계에서 공유를 방지하기 위해 사용됩니다.예를 들어, 단일 스레드 애플리케이션은 모든 메서드에 Connection을 전달할 필요가 없도록 시작 시 초기화된 글로벌 데이터베이스 연결을 유지할 수 있습니다.JDBC 접속은 스레드 세이프가 아닐 수 있으므로 추가 조정 없이 글로벌 접속을 사용하는 멀티스레드 어플리케이션도 스레드 세이프가 아닙니다.으로써 Connection에서 JDBC 접속을 저장합니다.리스트 3.10의 홀더, 각 스레드에는 독자적인 접속이 있습니다.
ThreadLocal은 응용 프로그램 프레임워크 구현에 널리 사용됩니다.예를 들어 J2EE 컨테이너는 EJB 콜 기간 동안 트랜잭션콘텍스트를 실행 스레드와 관련짓습니다.이는 트랜잭션 컨텍스트를 유지하는 정적 스레드 로컬을 사용하여 쉽게 구현할 수 있습니다. 프레임워크 코드가 현재 실행 중인 트랜잭션을 판별해야 할 경우 이 스레드 로컬에서 트랜잭션 컨텍스트를 가져옵니다.이는 실행 컨텍스트 정보를 모든 메서드에 전달할 필요성을 줄여주지만 이 메커니즘을 사용하는 모든 코드를 프레임워크에 결합한다는 점에서 편리합니다.
스레드 로컬의 스레드 제한 속성을 글로벌 변수를 사용하는 라이센스 또는 "숨김" 메서드 인수를 만드는 수단으로 취급함으로써 스레드 로컬을 악용하기 쉽습니다.글로벌 변수와 마찬가지로 스레드 로컬 변수는 재사용 가능성을 떨어뜨리고 클래스 간에 숨겨진 커플링을 발생시킬 수 있으므로 주의하여 사용해야 합니다.
기본적으로 현재 스레드에 의존하기 위해 변수 값이 필요하고 스레드에 값을 부가하는 것이 편리하지 않은 경우(예를 들어 스레드 하위 분류)입니다.
일반적인 예로는 서블릿 컨테이너와 같이 코드가 실행되고 있는 스레드를 다른 프레임워크에서 작성했거나 스레드 로컬을 사용하는 것이 더 의미가 있는 경우입니다.이는 변수가 스레드 서브클래스나 다른 해시 맵에 매달려 있는 것이 아니라 "논리적인 장소"에 있기 때문입니다.
제 웹사이트에서는 Thread Local을 언제 사용해야 하는지에 대한 자세한 설명과 예시가 있습니다.
스레드 번호가 필요한 특정 동시 알고리즘의 각 스레드에 "스레드 ID"를 부가하는 방법으로 ThreadLocal을 사용할 것을 권장하는 사람도 있습니다(예:Herlihy & Shavit)이런 경우, 실제로 혜택을 받고 있는지 확인해 보세요!
Java의 ThreadLocal은 JDK 1.2에서 도입되었지만 나중에 JDK 1.5에서 생성되어 ThreadLocal 변수에 대한 유형 안전성을 도입했습니다.
스레드 로컬은 스레드 범위와 관련지을 수 있습니다. 스레드에 의해 실행되는 모든 코드는 스레드 로컬 변수에 액세스할 수 있지만 두 스레드는 서로 스레드 로컬 변수를 볼 수 없습니다.
각 스레드는 스레드 로컬 변수의 배타적 복사본을 보유합니다.이 복사본은 스레드가 종료되거나 종료된 후(일반적으로 또는 예외로 인해) 가비지 컬렉션의 대상이 됩니다.이 경우 해당 스레드 로컬 변수에는 다른 라이브 참조가 없습니다.
Java의 ThreadLocal 변수는 일반적으로 클래스의 개인 정적 필드이며 스레드 내에서 상태를 유지합니다.
상세보기:Java의 스레드 로컬 - 프로그램 및 튜토리얼 예시
문서에는 "get 또는 set 메서드를 통해 [스레드 로컬 변수]에 액세스하는 각 스레드에는 변수의 독자적인 초기화된 복사본이 있습니다."라고 매우 잘 설명되어 있습니다.
각 스레드에 고유한 복사본이 있어야 할 때 하나를 사용합니다.기본적으로 데이터는 스레드 간에 공유됩니다.
앱풀과 를할 수 .ThreadLocal
해야 합니다.할 수 .var는 다음 요청으로 재사용할 수 있습니다.따라서 현재 스레드는 다음 요청에 의해 재사용될 수 있습니다.
할 수 두 가지 예: " " " " " " " 。
또는 ). 1- 는 사용자 ID 또는 트랜잭션 ID). 웹 웹 어플리케이션에서는 .연관된 아이디
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
이니셜 람다
다른 에 따른 많이 들기 때문에 입니다.2 - 른른 、 른른 、 른른 、 른른 、 른른 、 른른 、 른 2 、 2 2 2 2 。SimpleDateFormat 이이date 。SimpleDateFormat은 스레드 세이프가 아니기 때문에 스레드 세이프를 위한 메커니즘을 제공해야 합니다.
public class ThreadLocalDemo1 implements Runnable {
// threadlocal variable is created
private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
// Two threads are created
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + dateFormat.get().toPattern());
System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
}
}
8 이후 8을 하는 보다 했습니다.ThreadLocal
:
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
Java 8 릴리즈까지는 다음 작업을 수행해야 합니다.
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
한음음음음음음음음음음음음음음시시(시공자,에 되는 , 공장법)의 ThreadLocal
)를 할 수 .단순히 메서드레퍼런스(Java 8에서 도입)를 사용할 수 있습니다.
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
주의: 합격했기 때문에 평가는 귀찮습니다.java.util.function.Supplier
ThreadLocal#get
호출되었지만 값이 이전에 평가되지 않았습니다.
ThreadLocal 패턴은 매우 주의해야 합니다.필이 언급한 것과 같은 주요 단점이 몇 가지 있지만, 언급되지 않은 단점은 스레드 로컬 컨텍스트를 설정하는 코드가 "재진입"하지 않도록 하는 것입니다.
스레드 상의 정보가 예상치 못한 때에 변환되기 시작할 수 있기 때문에 정보를 설정하는 코드가 두 번째 또는 세 번째 실행되면 나쁜 일이 발생할 수 있습니다.따라서 스레드 로컬 정보를 다시 설정하기 전에 해당 정보가 설정되지 않았는지 확인하십시오.
ThreadLocal은 동기화되지 않은 메서드의 여러 스레드를 통해 변경 가능한 오브젝트에 확실하게 액세스합니다.즉, 변경 가능한 오브젝트를 메서드 내에서 불변하게 합니다.
이 작업은 각 스레드 시도 시마다 변경 가능한 개체의 새 인스턴스를 제공함으로써 수행됩니다. 각입니다.이것은 로컬 변수처럼 접근하기 위한 방법으로 인스턴스 변수를 만드는 해킹입니다.메서드 로컬 변수는 스레드에서만 사용할 수 있습니다.한 가지 차이점은 메서드 로컬 변수는 메서드 실행이 끝나면 스레드 로컬과 공유되는 가변 객체가 정리될 때까지 여러 메서드에서 사용할 수 있다는 것입니다.
정의별:
Java의 ThreadLocal 클래스를 사용하면 동일한 스레드로만 읽고 쓸 수 있는 변수를 만들 수 있습니다.따라서 두 스레드가 동일한 코드를 실행하고 있고 코드가 스레드 로컬 변수를 참조하고 있는 경우에도 두 스레드는 서로의 스레드 로컬 변수를 볼 수 없습니다.
★★Thread
contains in java contains in javaThreadLocalMap
안에.
서 ★★★★★
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
스레드 로컬 실현:
으로 다음과 () 를 유지하는 ThreadLocal 클래스를 .이 클래스는 다음과 같은 가변 객체를 보유합니다(유무에 관계없이).initialValue()
이제 이 래퍼의 getter 및 setter는 가변 객체가 아닌 스레드 로컬인스턴스에서 동작합니다.
가 threadlocal getter()의 스레드 맵에서 을 찾을 수 없는 Thread
; 다음으로 스레드에 대한 개인 복사본을 가져오기 위해 initialValue()를 호출합니다.
class SimpleDateFormatInstancePerThread {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
UUID id = UUID.randomUUID();
@Override
public String toString() {
return id.toString();
};
};
System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
return dateFormat;
}
};
/*
* Every time there is a call for DateFormat, ThreadLocal will return calling
* Thread's copy of SimpleDateFormat
*/
public static DateFormat getDateFormatter() {
return dateFormatHolder.get();
}
public static void cleanup() {
dateFormatHolder.remove();
}
}
, 이제wrapper.getDateFormatter()
를 호출합니다.threadlocal.get()
하면 '할 수 있다'가 됩니다.currentThread.threadLocalMap
에는 이(로컬) 인스턴스가 포함되어 있습니다.
이 yes로 됩니다.
그렇지 않으면 이 스레드 로컬인스턴스 initialValue()를 사용하여 맵을 추가합니다.
이 경우 이 변경 가능한 클래스에서 스레드 안전성이 확보되어 각 스레드는 자체 변경 가능한 인스턴스와 함께 작동하지만 동일한 스레드 로컬 인스턴스와 함께 작동합니다.모든 스레드가 동일한 스레드 로컬 인스턴스를 키로 공유하지만 다른 SimpleDateFormat 인스턴스를 값으로 공유함을 의미합니다.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
언제?
오브젝트가 스레드 세이프가 아닌 경우 scalability를 저해하는 동기화 대신 스레드 범위(ThreadLocal)를 각 스레드에 1개씩 부여하여 유지합니다.가장 자주 사용되지만 스레드세이프가 아닌 오브젝트 중 하나는 데이터베이스 Connection과 JMSConnection입니다.
어떻게?
예를 들어 Spring 프레임워크는 이러한 연결 개체를 ThreadLocal 변수에 유지함으로써 백그라운드에서 트랜잭션을 관리하기 위해 ThreadLocal을 많이 사용합니다.높은 레벨에서는 트랜잭션이 시작되면 연결이 확립되어(및 auto commit을 디세블로 하여) 스레드 로컬로 유지합니다.이후 DB 콜에서는 동일한 연결을 사용하여 DB와 통신합니다.마지막으로 ThreadLocal에서 연결을 가져와 트랜잭션을 커밋(또는 롤백)하고 연결을 해제합니다.
log4j는 MDC 유지보수에 Thread Local도 사용하고 있다고 생각합니다.
ThreadLocal
는 다른 스레드 간에 공유해서는 안 되는 상태를 유지할 때 편리하지만 라이프타임 동안 각 스레드에서 액세스할 수 있어야 합니다.
예를 들어, 각 요청이 서로 다른 스레드로 처리되는 웹 애플리케이션을 상상해 보십시오.각 요청에 대해 데이터를 여러 번 사용해야 하므로 계산 비용이 상당히 많이 든다고 가정해 보십시오.그러나 수신 요청마다 해당 데이터가 변경되었을 수 있습니다. 즉, 일반 캐시를 사용할 수 없습니다.에 대한은 " " "를 사용하는 입니다.ThreadLocal
이 데이터에 대한 액세스를 유지하는 변수이므로 각 요청에 대해 한 번만 계산하면 됩니다.이 ,, 음음음음음음음음음음음음음음음 of of 、 of 、 of 、 of 、 of 、 of 、 of 、 of the the 。ThreadLocal
일러스트레이션 목적으로 고안한 것입니다.
, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.ThreadLocal
는 기본적으로 글로벌스테이트의 한 형태입니다.그 결과, 다른 많은 의미를 가지며, 다른 모든 가능한 해결책을 고려한 후에만 사용해야 합니다.
멀티스레드 코드에서 SimpleDateFormat과 같은 클래스 도우미를 사용하는 시나리오는 3가지가 있으며, 그 중 가장 좋은 것은 ThreadLocal을 사용하는 것입니다.
시나리오
1-잠금 또는 동기화 메커니즘으로 앱을 느리게 하는 유사 공유 객체 사용
스레드 풀 시나리오
2- 메서드 내 로컬 객체로 사용
이 시나리오에서는 스레드 풀에서 4개의 스레드가 있는 경우 각각 1000개의 작업 시간이 있습니다.
4000개의 SimpleDateFormat 객체가 생성되어 GC가 삭제되기를 기다립니다.
3- 스레드 로컬 사용
스레드 풀에서 4개의 스레드가 있고 각 스레드에 1개의 SimpleDateFormat 인스턴스를 부여한 경우
4개의 스레드, 4개의 오브젝트 SimpleDateFormat이 있습니다.
잠금 메커니즘이나 오브젝트 생성 및 파괴가 필요하지 않습니다.(적절한 시간과 공간의 복잡성)
https://www.youtube.com/watch?v=sjMe9aecW_A
별로 게 , 오늘 알았어요.ThreadLocal
는 웹 응용 프로그램에서 Bean Validation을 사용할 때 매우 유용합니다.으로는 ""를 사용합니다.Locale.getDefault()
. 을할 수 있습니다Validator
MessageInterpolator
'아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아Locale
했을 때validate
인 것을 수 ThreadLocal<Locale>
좋은 당신이 로 하는 있는 입니다.)ThreadLocal
커스텀을 .MessageInterpolator
Locale
, 다음 는 'A로 하다'를 쓰는 거예요.ServletFilter
값 ""를 합니다.request.getLocale()
ThreadLocal
★★★★★★ 。
@unknown(google)에서 설명한 바와 같이 참조되는 값이 각 스레드에서 고유할 수 있는 글로벌 변수를 정의하는 것이 사용 방법입니다.일반적으로 이 방법은 현재 실행 스레드에 연결된 컨텍스트 정보를 저장하는 것을 수반합니다.
Java EE 환경에서 사용자 ID를 Java EE를 인식하지 못하는 클래스(HttpSession 또는 EJB SessionContext에 액세스할 수 없음)에 전달하기 위해 사용합니다.이렇게 하면 보안 기반 작업에 ID를 사용하는 코드가 모든 메서드콜에서 명시적으로 ID를 전달할 필요 없이 어디에서나 ID에 액세스할 수 있습니다.
대부분의 Java EE 호출에서 작업 요청/응답 주기는 스레드 로컬을 설정 및 설정 해제할 수 있는 잘 정의된 진입 및 종료 지점을 제공하기 때문에 이러한 유형의 사용을 쉽게 합니다.
스레드 로컬 변수는 종종 가변 싱글톤 또는 전역 변수를 기반으로 한 설계에서 공유를 방지하기 위해 사용됩니다.
Connection Pool을 사용하지 않을 때 각 스레드에 대해 별도의 JDBC 연결을 만드는 등의 시나리오에서 사용할 수 있습니다.
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
getConnection을 호출하면 해당 스레드와 관련된 연결이 반환됩니다.스레드 간에 공유하지 않을 날짜 형식, 트랜잭션 컨텍스트와 같은 다른 속성에서도 동일한 작업을 수행할 수 있습니다.
로컬 변수를 같은 용도로 사용할 수도 있지만, 이러한 리소스는 일반적으로 작성에 시간이 걸리기 때문에 비즈니스 로직을 수행할 때마다 반복해서 생성하기를 원하지 않습니다.그러나 ThreadLocal 값은 스레드 개체 자체에 저장되며 스레드가 가비지 수집되는 즉시 이러한 값도 사라집니다.
이 링크는 ThreadLocal 사용에 대해 잘 설명합니다.
캐싱에서는 같은 값의 로트 시간을 계산해야 하는 경우가 있기 때문에 마지막 입력 세트를 메서드에 저장하고 그 결과를 코드 속도를 높일 수 있습니다.스레드 로컬 스토리지를 사용하면 잠금에 대해 생각할 필요가 없습니다.
Thread Local은 스레드 전용 격리된 스토리지 공간을 제공하기 위해 JVM에 의해 특별히 프로비저닝된 기능입니다.instance scope 변수 값이 클래스의 특정 인스턴스에만 바인딩됩니다.각 개체에는 값만 있고 서로 값을 볼 수 없습니다.ThreadLocal 변수의 개념도 마찬가지입니다.이러한 변수는 스레드를 작성한 스레드 이외의 다른 스레드에서는 스레드를 볼 수 없습니다.여기를 참조해 주세요.
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
}
}
Java의 ThreadLocal 클래스를 사용하면 동일한 스레드로만 읽고 쓸 수 있는 변수를 만들 수 있습니다.따라서 두 스레드가 동일한 코드를 실행하고 있고 코드가 스레드 로컬 변수를 참조하고 있는 경우에도 두 스레드는 서로의 스레드 로컬 변수를 볼 수 없습니다.
[참고용]ThreadLocal은 공유 객체의 업데이트 문제를 해결할 수 없습니다.동일한 스레드의 모든 작업에서 공유되는 staticThreadLocal 개체를 사용하는 것이 좋습니다.[필수] remove() 메서드는 스레드가 자주 재사용되는 스레드 풀을 사용하는 경우 ThreadLocal 변수를 사용하여 구현해야 합니다.그렇지 않으면 이후의 비즈니스 로직에 영향을 미쳐 메모리 누수 등의 예기치 않은 문제가 발생할 수 있습니다.
Threadlocal은 비용을 들이지 않고 오브젝트를 재사용할 수 있는 매우 쉬운 방법을 제공합니다.
업데이트 알림마다 여러 스레드가 변경 가능한 캐시 이미지를 생성하는 상황이 발생했습니다.
각 스레드에서 스레드 로컬을 사용했는데, 각 스레드는 이전 이미지를 리셋하고 각 업데이트 알림에서 캐시에서 다시 업데이트하면 됩니다.
오브젝트 풀의 일반적인 재사용 가능한 오브젝트에는 스레드안전비용이 관련지어져 있습니다만, 이 접근방식은 없습니다.
ThreadLocal 변수의 느낌을 얻으려면 다음 작은 예를 사용해 보십시오.
public class Book implements Runnable {
private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);
private final String bookName; // It is also the thread's name
private final List<String> words;
public Book(String bookName, List<String> words) {
this.bookName = bookName;
this.words = Collections.unmodifiableList(words);
}
public void run() {
WORDS.get().addAll(words);
System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
}
public static void main(String[] args) {
Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
t1.start();
t2.start();
}
}
★★★★북A:
A: 'A1, A2, A3'
§ B권 : '단어 B1, 단어 B2'
Book B는 다음과 같습니다.
§ B권 : '단어 B1, 단어 B2'
A: 'A1, A2, A3'
첫 번째 사용 사례 - 스레드의 안전성과 성능을 제공하는 스레드별 컨텍스트 Spring Framework 클래스의 실시간 예시 -
- Locale Context Holder(Locale Context Holder)
- Transaction Context Holder(트랜잭션 컨텍스트 홀더)
- Request Context Holder(Request Context Holder)
- Date Time Context Holder(날짜 시간 컨텍스트 홀더)
두 번째 사용 사례 - 스레드 간에 무언가를 공유하지 않고 동시에 성능 비용 때문에 동기화/잠금 기능을 사용하지 않는 경우 - SimpleDateFormat을 사용하여 날짜의 커스텀 형식을 만듭니다.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author - GreenLearner(https://www.youtube.com/c/greenlearner)
*/
public class ThreadLocalDemo1 {
SimpleDateFormat sdf = new SimpleDateFormat("dd-mm-yyyy");//not thread safe
ThreadLocal<SimpleDateFormat> tdl1 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-dd-mm"));
public static void main(String[] args) {
ThreadLocalDemo1 d1 = new ThreadLocalDemo1();
ExecutorService es = Executors.newFixedThreadPool(10);
for(int i=0; i<100; i++) {
es.submit(() -> System.out.println(d1.getDate(new Date())));
}
es.shutdown();
}
String getDate(Date date){
// String s = tsdf.get().format(date);
String s1 = tdl1.get().format(date);
return s1;
}
}
사용상의 힌트
- 가능한 경우 로컬 변수를 사용합니다.이렇게 하면 스레드 로컬을 사용하지 않아도 됩니다.
- 기능을 가능한 한 프레임워크에 위임합니다.
- 스레드 로컬을 사용하고 상태를 스레드 로컬로 설정한 경우, 그렇지 않은 경우 반드시 스레드 로컬을 클리어해야 합니다.그것이 Out Of Memory Error의 주요 원인이 될 수 있습니다.
언급URL : https://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable
'programing' 카테고리의 다른 글
POSIX 스레드 및 신호 (0) | 2022.07.08 |
---|---|
C에서 "함수에 대한 충돌 유형"을 얻을 수 있습니다. 이유는 무엇입니까? (0) | 2022.07.08 |
링크 리스트에서 루프를 검출하려면 어떻게 해야 합니다. (0) | 2022.07.08 |
std::ios_base:에 대한 정의되지 않은 참조:초기화::Init()' (0) | 2022.07.08 |
vue.js 및 Auth0을 사용하여 사용자가 이미 인증되어 있는 경우 로그인 페이지를 건너뛰려면 어떻게 해야 합니까? (0) | 2022.07.08 |