언제 ThreadLocal
변수를 사용해야 합니까?
어떻게 사용 되나요?
RequestUtils.getCurrentRequestThreadLocal()
. 비록 이것이 매우 우아하다고 말하지는 않지만, 이것은 ThreadLocal 자체가 대부분의 경우에 매우 우아하지 않다는 사실에서 비롯됩니다.
언제 ThreadLocal
변수를 사용해야 합니까?
어떻게 사용 되나요?
RequestUtils.getCurrentRequestThreadLocal()
. 비록 이것이 매우 우아하다고 말하지는 않지만, 이것은 ThreadLocal 자체가 대부분의 경우에 매우 우아하지 않다는 사실에서 비롯됩니다.
답변:
스레드에 안전하지 않은 객체가 있지만 해당 객체에 대한 액세스를 동기화 하지 않으려는 경우 (일반적으로) 사용할 수 있습니다 ( 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);
}
}
문서 .
SimpleDateFormat
입니다. 아마도 스레드 안전 대안 을 사용 하는 것이 좋습니다 . 당신이 동의하는 경우 싱글 나쁜 후 ThreadLocal
더 악화입니다.
a ThreadLocal
는 지정된 내의 데이터에 대한 참조 이므로 스레드 풀을 사용하는 응용 프로그램 서버에서 s를 Thread
사용할 때 클래스 로딩 누수가 발생할 수 ThreadLocal
있습니다. 당신은 어떤 청소에 대해 매우 신중해야 ThreadLocal
당신을들 get()
또는 set()
사용하여 ThreadLocal
의 remove()
방법을.
완료 할 때 정리하지 않으면 배포 된 웹앱의 일부로로드 된 클래스에 대한 참조는 영구 힙에 유지되며 가비지 수집을받지 않습니다. webapp을 재배포 / 배포 해제하면 webapp이 소유 한 것이 아니기 Thread
때문에 webapp의 클래스에 대한 각 참조를 정리 Thread
하지 않습니다. 각 연속 배포는 가비지 수집되지 않는 클래스의 새 인스턴스를 만듭니다.
java.lang.OutOfMemoryError: PermGen space
일부 인터넷 검색 으로 인해 -XX:MaxPermSize
버그가 수정되지 않고 메모리 부족 예외가 발생할 수 있습니다.
이러한 문제점이 발생하면 Eclipse의 메모리 분석기 를 사용 하거나 Frank Kieviet의 안내서 및 후속 조치를 통해 이러한 참조를 보유하는 스레드 및 클래스를 판별 할 수 있습니다 .
업데이트 : Alex Vasseur의 블로그 항목 을 다시 발견 하여 ThreadLocal
내가 겪고 있는 문제를 추적하는 데 도움 이되었습니다.
많은 프레임 워크는 ThreadLocals를 사용하여 현재 스레드와 관련된 일부 컨텍스트를 유지합니다. 예를 들어, 현재 트랜잭션이 ThreadLocal에 저장 될 때 스택을 다운받는 누군가가 액세스해야 할 경우를 대비하여 모든 메소드 호출을 통해 매개 변수로 전달할 필요는 없습니다. 웹 응용 프로그램은 현재 요청 및 세션에 대한 정보를 ThreadLocal에 저장하여 응용 프로그램이 쉽게 액세스 할 수 있도록합니다. Guice를 사용 하면 주입 된 객체에 대한 사용자 지정 범위 를 구현할 때 ThreadLocals를 사용할 수 있습니다 (Guice의 기본 서블릿 범위는 대부분 사용합니다).
ThreadLocals는 일종의 전역 변수 (하나의 스레드로 제한되기 때문에 약간 덜 악하지만), 원하지 않는 부작용과 메모리 누수를 피하기 위해 사용할 때주의해야합니다. ThreadLocal 값이 더 이상 필요하지 않을 때 항상 자동으로 지워지고 API를 잘못 사용할 수 없도록 API를 설계하십시오 (예 : ). ThreadLocals를 사용하여 코드를 더 깨끗하게 만들 수 있으며, 드물게 작동하는 유일한 방법입니다. 현재 프로젝트에는 두 가지 경우가 있습니다. 여기 에는 "정적 필드 및 전역 변수"에 설명되어 있습니다.
Java에서 스레드마다 다를 수있는 데이텀이있는 경우 해당 데이텀을 필요한 (또는 필요할 수있는) 모든 메소드로 전달하거나 데이텀을 스레드와 연관시키는 선택이 있습니다. 모든 방법이 이미 공통 "컨텍스트"변수를 통과해야하는 경우 모든 곳에서 데이텀을 전달하는 것이 가능할 수 있습니다.
그렇지 않은 경우 추가 매개 변수로 메소드 서명을 어지럽히 지 않을 수 있습니다. 스레드되지 않은 세계에서는 전역 변수에 해당하는 Java 문제를 해결할 수 있습니다. 스레드 단어에서 전역 변수에 해당하는 것은 스레드 로컬 변수입니다.
'datum' is the singular form and 'data' is the plural form.
Java Concurrency in Practice 책에는 좋은 예가 있습니다. 저자 ( Joshua Bloch )는 스레드 제한이 스레드 안전성을 달성하는 가장 간단한 방법 중 하나 인 방법을 설명하고 ThreadLocal 은 스레드 제한을 유지하는보다 공식적인 방법입니다. 결국 그는 사람들이 그것을 글로벌 변수로 사용하여 어떻게 남용 할 수 있는지 설명합니다.
언급 한 책에서 텍스트를 복사했지만 ThreadLocal을 사용해야하는 곳을 이해하는 것이별로 중요하지 않으므로 코드 3.10이 누락되었습니다.
스레드 로컬 변수는 종종 가변 단일 톤 또는 전역 변수를 기반으로하는 설계에서 공유를 방지하는 데 사용됩니다. 예를 들어 단일 스레드 응용 프로그램은 시작시 초기화되는 전역 데이터베이스 연결을 유지하여 모든 메서드에 Connection을 전달하지 않아도됩니다. JDBC 연결은 스레드로부터 안전하지 않을 수 있으므로 추가 조정없이 전역 연결을 사용하는 다중 스레드 응용 프로그램도 스레드로부터 안전하지 않습니다. Listing 3.10의 ConnectionHolder에서와 같이 ThreadLocal을 사용하여 JDBC 연결을 저장하면 각 스레드는 자체 연결을 갖게된다.
ThreadLocal은 응용 프로그램 프레임 워크 구현에 널리 사용됩니다. 예를 들어, J2EE 컨테이너는 EJB 호출 기간 동안 트랜잭션 컨텍스트를 실행 스레드와 연관시킵니다. 이것은 트랜잭션 컨텍스트를 보유하는 정적 Thread-Local을 사용하여 쉽게 구현됩니다. 프레임 워크 코드는 현재 실행중인 트랜잭션을 결정해야 할 때이 ThreadLocal에서 트랜잭션 컨텍스트를 가져옵니다. 이는 실행 컨텍스트 정보를 모든 메소드에 전달할 필요가 줄어들지 만이 메커니즘을 사용하는 모든 코드를 프레임 워크에 결합한다는 점에서 편리합니다.
스레드 제한 속성을 전역 변수 사용 라이센스로 처리하거나 "숨겨진"메서드 인수를 만드는 수단으로 ThreadLocal을 악용하기 쉽습니다. 전역 변수와 마찬가지로 스레드 지역 변수는 재사용 성을 떨어 뜨리고 클래스간에 숨겨진 커플 링을 유발할 수 있으므로주의해서 사용해야합니다.
기본적으로, 당신은 필요로 할 때 , 현재의 thread에 의존 변수의 값을 하고 당신이 다른 방법으로 스레드에 가치를 부여하는 편리하지 않습니다 (스레드를 서브 클래스, 예를 들어).
일반적인 경우는 다른 프레임 워크가 스레드를 생성 한 경우입니다. 코드가 실행중인 예 : 서블릿 컨테이너)를 생성 한 경우 또는 변수가 "논리적 위치"(변수가 아닌) "논리적 위치"에 있기 때문에 ThreadLocal을 사용하는 것이 더 의미가있는 경우입니다. Thread 서브 클래스 또는 다른 해시 맵에 매달려 있습니다).
내 웹 사이트에서 ThreadLocal 을 언제 사용 해야하는지에 대한 추가 토론과 예가 있습니다.
어떤 사람들은 스레드 번호가 필요한 특정 동시 알고리즘에서 각 스레드에 "스레드 ID"를 연결하는 방법으로 ThreadLocal을 사용하도록 권장합니다 (예 : Herlihy & Shavit 참조). 그러한 경우, 실제로 혜택을 받고 있는지 확인하십시오!
Java의 ThreadLocal은 JDK 1.2에 도입되었지만 나중에 JDK 1.5에서 생성되어 ThreadLocal 변수에 유형 안전성을 도입했습니다.
ThreadLocal은 Thread 범위와 연관 될 수 있습니다. Thread에 의해 실행되는 모든 코드는 ThreadLocal 변수에 액세스 할 수 있지만 두 개의 스레드는 서로 스레드 ThreadLocal 변수를 볼 수 없습니다.
각 스레드에는 ThreadLocal 변수의 독점 사본이 있습니다. 스레드 로컬 변수에는 스레드가 완료되거나 종료 된 후 (일반적으로 또는 예외로 인해) 가비지 콜렉션에 적합하게됩니다. ThreadLocal 변수에는 다른 라이브 참조가 없습니다.
Java의 ThreadLocal 변수는 일반적으로 클래스의 전용 정적 필드이며 Thread 내에서 상태를 유지합니다.
이 문서 는 다음과 같이 잘 설명합니다. "[스레드 로컬 변수]에 액세스하는 각 스레드 (get 또는 set 메소드를 통해)에는 자체적으로 독립적으로 초기화 된 변수 사본이 있습니다."
각 스레드에 고유 한 사본이 있어야 할 때 하나를 사용하십시오. 기본적으로 데이터는 스레드간에 공유됩니다.
threadlocal 변수를 사용할 수있는 두 가지 사용 사례
-1- 상태를 스레드와 연결해야하는 경우 (예 : 사용자 ID 또는 트랜잭션 ID) 이는 일반적으로 서블릿으로가는 모든 요청에 고유 한 transactionID가있는 웹 응용 프로그램에서 발생합니다.
// 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();
}
}
여기서 initial을 사용한 메소드는 람다 식을 사용하여 구현됩니다.
2- 또 다른 유스 케이스는 스레드 안전 인스턴스를 원하고 동기화 성능 비용이 높기 때문에 동기화를 사용하지 않는 경우입니다. 이러한 경우 중 하나가 SimpleDateFormat을 사용하는 경우입니다. 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()));
}
}
Java 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 패턴에 매우주의해야합니다. Phil과 같은 주요 단점이 있지만 언급되지 않은 것은 ThreadLocal 컨텍스트를 설정하는 코드가 "재진입"하지 않도록하는 것입니다.
정보를 설정하는 코드가 두 번째 또는 세 번째로 실행될 때 예상치 못한 스레드 정보가 변경 될 수 있기 때문에 나쁜 일이 발생할 수 있습니다. 따라서 다시 설정하기 전에 ThreadLocal 정보가 설정되지 않았는지 확인하십시오.
ThreadLocal
패턴 뿐만 아니라 작성하는 모든 코드에도 적용됩니다 . 당신이 F(){ member=random(); F2(); write(member); }
하고 F2가 새로운 값으로 멤버를 재정의하면 분명히 write(member)
더 이상 당신이 작성한 번호를 쓰지 않을 것 random()
입니다. 이것은 말 그대로 상식입니다. 마찬가지로, 그렇게하면 F(){ F(); }
무한 루프로 행운을 빕니다! 이것은 모든 곳에서 사실이며 특정하지 않습니다 ThreadLocal
.
언제?
객체가 스레드로부터 안전하지 않은 경우, 확장 성을 방해하는 동기화 대신 모든 객체에 하나의 객체를 제공하고 스레드 범위 (ThreadLocal)를 유지합니다. 가장 안전하지만 스레드로부터 안전하지 않은 오브젝트 중 하나는 데이터베이스 연결 및 JMSConnection입니다.
어떻게 ?
한 가지 예는 Spring 프레임 워크가 ThreadLocal 변수에 이러한 연결 오브젝트를 유지하여 백그라운드에서 트랜잭션을 관리하기 위해 ThreadLocal을 많이 사용하는 것입니다. 높은 수준에서 트랜잭션이 시작되면 연결을 얻고 자동 커밋을 비활성화하고 ThreadLocal에 유지합니다. 추가 DB 호출에서 동일한 연결을 사용하여 db와 통신합니다. 결국, ThreadLocal에서 연결을 가져 와서 트랜잭션을 커밋 (또는 롤백)하고 연결을 해제합니다.
log4j는 MDC를 유지하기 위해 ThreadLocal을 사용한다고 생각합니다.
ThreadLocal
다른 스레드간에 공유해서는 안되는 상태를 원하지만 전체 수명 동안 각 스레드에서 액세스 할 수 있어야하는 경우 유용합니다.
예를 들어, 각 요청이 다른 스레드에서 제공되는 웹 응용 프로그램을 상상해보십시오. 각 요청마다 여러 번의 데이터 조각이 필요하다고 가정하면 계산하는 데 비용이 많이 듭니다. 그러나 들어오는 각 요청에 대해 해당 데이터가 변경되었을 수 있으므로 일반 캐시를 사용할 수 없습니다. 이 문제에 대한 간단하고 빠른 해결책은이 ThreadLocal
데이터에 대한 액세스 권한을 보유한 변수를 사용하여 각 요청에 대해 한 번만 계산해야합니다. 물론이 문제는 다음을 사용하지 않고도 해결할 수 있습니다.ThreadLocal
있지만 설명을 위해 고안했습니다.
ThreadLocal
s는 본질적으로 세계 국가의 한 형태 라는 것을 명심하십시오 . 결과적으로 다른 영향을 많이 미치므로 가능한 모든 다른 솔루션을 고려한 후에 만 사용해야합니다.
ThreadLocal
경우 객체 필드로 사용할 수 있습니다 .
ThreadLocal
전 세계에서 액세스 할 수있는 의 여러 가지 오용을 우연히 발견했을 것입니다 . 말했듯이 가시성을 제한하는 클래스 필드로 계속 사용할 수 있습니다.
여기에 새로운 것은 없지만 오늘 ThreadLocal
웹 응용 프로그램에서 Bean Validation을 사용할 때 매우 유용하다는 것을 알았습니다 . 유효성 검사 메시지는 현지화되어 있지만 기본적으로을 사용 Locale.getDefault()
합니다. 를 Validator
다른 것으로 구성 할 수 MessageInterpolator
있지만 Locale
호출 할 때 시간 을 지정할 수있는 방법이 없습니다 validate
. 따라서 정적 ThreadLocal<Locale>
(또는 더 나은 아직 다른 것들이있는 일반 컨테이너를 ThreadLocal
만든 다음 사용자 정의를 MessageInterpolator
선택할 수 Locale
있습니다. 다음 단계는 ServletFilter
세션 값을 사용하거나 request.getLocale()
로케일 및 저장소를 선택하는 것을 작성하는 것 입니다 그것은 당신에 ThreadLocal
대한 참조입니다.
@unknown (google)에서 언급했듯이 참조 된 값이 각 스레드에서 고유 할 수있는 전역 변수를 정의하는 것이 사용법입니다. 일반적으로 현재 실행 스레드에 연결된 일종의 상황 정보를 저장하는 것이 사용됩니다.
Java EE 환경에서이를 사용하여 Java EE를 인식하지 못하는 클래스 (HttpSession 또는 EJB SessionContext에 액세스 할 수 없음)에 사용자 ID를 전달합니다. 이런 식으로 보안 기반 작업에 ID를 사용하는 코드는 모든 메소드 호출에서 명시 적으로 전달하지 않고도 어디서나 ID에 액세스 할 수 있습니다.
대부분의 Java EE 호출에서 요청 / 응답주기는 이러한 유형의 사용법을 쉽게 만들어 ThreadLocal을 설정 및 설정 해제 할 수있는 잘 정의 된 시작 및 종료 지점을 제공합니다.
ThreadLocal은 동기화되지 않은 메소드의 여러 스레드에 의해 변경 가능한 오브젝트에 대한 액세스를 보장하므로 변경 가능한 오브젝트를 메소드 내에서 변경할 수 없게합니다.
이것은 각 스레드에 대해 변경 가능한 객체의 새로운 인스턴스를 제공함으로써 달성됩니다. 따라서 각 스레드에 대한 로컬 사본입니다. 이것은 로컬 변수처럼 액세스 할 수있는 메소드에서 인스턴스 변수를 만드는 데 약간의 해킹입니다. 메소드 로컬 변수는 스레드에서만 사용할 수 있다는 것을 알고 있으므로 한 가지 차이점이 있습니다. 일단 메소드 실행이 끝나면 thread 로컬에서 공유 할 수있는 가변 객체가 여러 메소드에서 정리 될 때까지 메소드 로컬 변수를 사용할 수 없습니다.
정의에 의해 :
Java의 ThreadLocal 클래스를 사용하면 동일한 스레드에서만 읽고 쓸 수있는 변수를 작성할 수 있습니다. 따라서 두 스레드가 동일한 코드를 실행하고 코드에 ThreadLocal 변수에 대한 참조가 있어도 두 스레드는 서로의 ThreadLocal 변수를 볼 수 없습니다.
Thread
Java의 각각 에는 포함 ThreadLocalMap
되어 있습니다.
어디
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()
) 변경 가능한 객체를 보유 할 ThreadLocal에 대한 래퍼 클래스를 만듭니다 .
이제이 래퍼의 getter와 setter는 변경 가능한 객체 대신 threadlocal 인스턴스에서 작동합니다.
threadlocal의 getter ()가의 threadlocalmap에서 값을 찾지 못한 경우 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()
하고 이것이 (threadlocal) 인스턴스를 currentThread.threadLocalMap
포함 하는지 확인합니다 .
yes 인 경우 해당 threadlocal 인스턴스에 대한 값 (SimpleDateFormat)을 리턴하고
그렇지 않으면이 threadlocal 인스턴스 인 initialValue ()로 맵을 추가하십시오.
이 변경 가능한 클래스에서 스레드 안전성을 달성했습니다. 각 스레드별로 자체 가변 인스턴스로 작업하지만 동일한 ThreadLocal 인스턴스로 작업합니다. 의미 모든 스레드는 키와 동일한 ThreadLocal 인스턴스를 공유하지만 값과는 다른 SimpleDateFormat 인스턴스를 공유합니다.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
스레드 로컬 변수는 종종 가변 단일 톤 또는 전역 변수를 기반으로하는 설계에서 공유를 방지하는 데 사용됩니다.
연결 풀을 사용하지 않을 때 각 스레드에 대해 별도의 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을 호출하면 해당 스레드와 관련된 연결이 반환됩니다. 스레드와 공유하고 싶지 않은 dateformat, 트랜잭션 컨텍스트와 같은 다른 속성을 사용하여 동일한 연결을 수행 할 수 있습니다.
로컬 변수를 동일하게 사용할 수도 있지만 이러한 리소스는 일반적으로 생성하는 데 시간이 걸리므로 비즈니스 로직을 수행 할 때마다 다시 생성하지 않으려는 경우가 있습니다. 그러나 ThreadLocal 값은 스레드 객체 자체에 저장되며 스레드가 가비지 수집되는 즉시 이러한 값도 사라집니다.
이 링크 는 ThreadLocal 사용법을 잘 설명합니다.
사용하기위한 3 가지 시나리오가 있습니다 클래스 헬퍼를 최고의 하나는 사용하는 것입니다 멀티 스레드 코드에서 SimpleDateFormat에, 같이가 의 ThreadLocal
시나리오
1 같이 사용하여 공유 객체 의 도움으로 잠금 또는 동기화 앱을 만드는 메커니즘 느린
2- 메소드 내에서 로컬 객체 로 사용
우리가이 시나리오에서, 4 스레드를 각각 하나의 방법 1000 호출 후 우리는 한 시간에
4000 SimpleDateFormat의의 객체 생성 및 GC 그들을 삭제 대기를
3- ThreadLocal 사용
우리는 4 스레드를 가지고 있다면 우리는 준 각 스레드 하나의 SimpleDateFormat의 인스턴스
우리는 그래서 4 개 스레드 , 4 객체 SimpleDateFormat에의합니다.
잠금 메커니즘과 객체 생성 및 소멸이 필요하지 않습니다. (좋은 시간 복잡성과 공간 복잡성)
ThreadLocal은 스레드에 대해서만 격리 된 스토리지 공간을 제공하기 위해 JVM에서 특별히 프로비저닝 된 기능입니다. 인스턴스 범위 변수의 값과 같이 클래스의 주어진 인스턴스에만 바인딩됩니다. 각 객체에는 유일한 값이 있으며 서로 다른 값을 볼 수 없습니다. 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();
}
}
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();
}
}
스레드 BookA가 먼저 수행되는 경우 콘솔 출력 :
결과 BookA : 'wordA1, wordA2, wordA3'.
결과 BookB : 'wordB1, wordB2'.
스레드 BookB가 먼저 수행되는 경우 콘솔 출력 :
결과 BookB : 'wordB1, wordB2'.
결과 북 A : 'wordA1, wordA2, wordA3'.