ThreadLocal 변수는 언제 어떻게 사용해야합니까?
언제 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);
}
}
문서 .
a ThreadLocal
는 주어진 Thread
에서 데이터에 대한 참조 이므로 ThreadLocal
스레드 풀을 사용하는 애플리케이션 서버에서 s를 사용할 때 클래스 로딩 누수가 발생할 수 있습니다. 당신은 어떤 청소에 대해 매우 신중해야 ThreadLocal
당신을들 get()
또는 set()
사용하여 ThreadLocal
의 remove()
방법을.
완료했을 때 정리하지 않으면 배포 된 웹앱의 일부로로드 된 클래스에 대한 참조가 영구 힙에 유지되고 가비지 수집되지 않습니다. 웹앱을 재배포 / 배포 해제 Thread
해도 웹앱이 Thread
소유 한 것이 아니기 때문에 웹앱 클래스에 대한 각 참조가 정리 되지 않습니다. 각 연속 배포는 가비지 수집되지 않는 클래스의 새 인스턴스를 만듭니다.
java.lang.OutOfMemoryError: PermGen space
일부 인터넷 검색 으로 인해 메모리 부족 예외가 발생 -XX:MaxPermSize
하고 버그를 수정하는 대신 증가 할 것입니다 .
이러한 문제가 발생하면 Eclipse의 Memory Analyzer 를 사용 하거나 Frank Kieviet의 가이드 및 후속 조치를 따라 이러한 참조를 보유하고있는 스레드와 클래스를 확인할 수 있습니다 .
업데이트 : 내가 겪고있는 몇 가지 문제를 추적하는 데 도움이되는 Alex Vasseur의 블로그 항목 을 다시 발견했습니다 ThreadLocal
.
많은 프레임 워크는 ThreadLocals를 사용하여 현재 스레드와 관련된 일부 컨텍스트를 유지합니다. 예를 들어 현재 트랜잭션이 ThreadLocal에 저장되면 스택 아래의 누군가가 액세스해야하는 경우를 대비하여 모든 메서드 호출을 통해 매개 변수로 전달할 필요가 없습니다. 웹 응용 프로그램은 현재 요청 및 세션에 대한 정보를 ThreadLocal에 저장할 수 있으므로 응용 프로그램에서 쉽게 액세스 할 수 있습니다. Guice를 사용 하면 삽입 된 객체에 대한 사용자 정의 범위 를 구현할 때 ThreadLocals를 사용할 수 있습니다 (Guice의 기본 서블릿 범위도 대부분이를 사용합니다).
ThreadLocals는 하나의 전역 변수 (하나의 스레드로 제한되기 때문에 약간 덜 사악함)이므로 원하지 않는 부작용과 메모리 누수를 방지하기 위해 사용할 때주의해야합니다. ThreadLocal 값이 더 이상 필요하지 않을 때 항상 자동으로 지워지고 API의 잘못된 사용이 불가능하도록 API를 설계합니다 (예 : this ). ThreadLocals는 코드를 더 깔끔하게 만드는 데 사용할 수 있으며, 드물게 작동하는 유일한 방법입니다 (현재 프로젝트에는 두 가지 경우가 있습니다. 여기 에 "Static Fields and Global Variables"아래에 설명되어 있습니다 ).
Java에서 스레드별로 달라질 수있는 데이텀이있는 경우 해당 데이텀을 필요로하는 (또는 필요할 수있는) 모든 메서드에 전달하거나 데이텀을 스레드와 연결하는 것입니다. 모든 방법이 이미 공통 "컨텍스트"변수를 전달해야하는 경우 모든 곳에서 데이텀을 전달할 수 있습니다.
그렇지 않은 경우 추가 매개 변수로 메서드 서명을 복잡하게 만들고 싶지 않을 수 있습니다. 스레드되지 않은 세계에서는 전역 변수에 해당하는 Java로 문제를 해결할 수 있습니다. 스레드 단어에서 전역 변수에 해당하는 것은 스레드 로컬 변수입니다.
Java Concurrency in Practice 책에 아주 좋은 예가 있습니다. 작성자 ( Joshua Bloch )가 스레드 제한이 스레드 안전성을 달성하는 가장 간단한 방법 중 하나이며 ThreadLocal 이 스레드 제한을 유지하는보다 공식적인 방법을 설명하는 곳입니다 . 결국 그는 사람들이 그것을 전역 변수로 사용하여 어떻게 남용 할 수 있는지 설명합니다.
언급 된 책의 텍스트를 복사했지만 ThreadLocal을 사용해야하는 위치를 이해하는 것이 그다지 중요하지 않기 때문에 코드 3.10이 누락되었습니다.
스레드 로컬 변수는 종종 변경 가능한 싱글 톤 또는 전역 변수를 기반으로하는 디자인에서 공유를 방지하는 데 사용됩니다. 예를 들어 단일 스레드 응용 프로그램은 모든 메서드에 Connection을 전달하지 않아도되도록 시작시 초기화되는 전역 데이터베이스 연결을 유지할 수 있습니다. JDBC 연결은 스레드로부터 안전하지 않을 수 있으므로 추가 조정없이 전역 연결을 사용하는 다중 스레드 응용 프로그램도 스레드로부터 안전하지 않습니다. 목록 3.10의 ConnectionHolder 에서처럼 ThreadLocal을 사용하여 JDBC 연결을 저장하면 각 스레드가 자체 연결을 갖게됩니다.
ThreadLocal은 응용 프로그램 프레임 워크 구현에 널리 사용됩니다. 예를 들어, J2EE 컨테이너는 EJB 호출 기간 동안 트랜잭션 컨텍스트를 실행 스레드와 연결합니다. 이것은 트랜잭션 컨텍스트를 보유하는 정적 스레드 로컬을 사용하여 쉽게 구현됩니다. 프레임 워크 코드가 현재 실행중인 트랜잭션을 확인해야 할 때이 ThreadLocal에서 트랜잭션 컨텍스트를 가져옵니다. 이는 실행 컨텍스트 정보를 모든 메서드에 전달할 필요성을 줄여 주지만이 메커니즘을 사용하는 모든 코드를 프레임 워크에 연결한다는 점에서 편리합니다.
스레드 제한 속성을 전역 변수를 사용하기위한 라이센스 또는 "숨겨진"메서드 인수를 만드는 수단으로 취급하여 ThreadLocal을 남용하기 쉽습니다. 전역 변수와 마찬가지로 스레드 로컬 변수는 재사용 성을 떨어 뜨리고 클래스간에 숨겨진 결합을 도입 할 수 있으므로주의해서 사용해야합니다.
본질적으로 현재 스레드에 의존 하는 변수 값 이 필요하고 다른 방법 (예 : 스레드 서브 클래 싱) 으로 스레드에 값을 연결하는 것이 편리하지 않은 경우 입니다 .
일반적인 경우는 다른 프레임 워크가 코드가 실행중인 스레드 ( 예 : 서블릿 컨테이너)를 생성했거나 변수가 "논리적 위치"(변수가 아닌)에 있기 때문에 ThreadLocal을 사용하는 것이 더 합리적인 경우입니다. Thread 하위 클래스 또는 다른 해시 맵에서 매달려 있음).
내 웹 사이트에는 ThreadLocal을 사용해야 할 때에 대한 추가 논의와 예가 있습니다.
어떤 사람들은 스레드 번호가 필요한 특정 동시 알고리즘에서 각 스레드에 "스레드 ID"를 첨부하는 방법으로 ThreadLocal을 사용하는 것을 옹호합니다 (예 : Herlihy & Shavit 참조). 그러한 경우에는 실제로 혜택을 받고 있는지 확인하십시오!
Java의 ThreadLocal은 JDK 1.2에 도입되었지만 나중에 ThreadLocal 변수에 대한 유형 안전성을 도입하기 위해 JDK 1.5에서 생성되었습니다.
ThreadLocal은 Thread 범위와 연관 될 수 있으며, Thread에 의해 실행되는 모든 코드는 ThreadLocal 변수에 액세스 할 수 있지만 두 스레드는 서로 ThreadLocal 변수를 볼 수 없습니다.
각 스레드는 스레드가 완료되거나 종료 된 후 일반적으로 또는 예외로 인해 가비지 수집에 적합하게되는 ThreadLocal 변수의 배타적 복사본을 보유합니다. 해당 ThreadLocal 변수에는 다른 라이브 참조가 없습니다.
Java의 ThreadLocal 변수는 일반적으로 Classes의 개인 정적 필드이며 Thread 내에서 상태를 유지합니다.
자세히보기 : Java의 ThreadLocal-예제 프로그램 및 자습서
문서 는 "[스레드 로컬 변수] (get 또는 set 메소드를 통해)에 액세스하는 각 스레드는 자체적으로 초기화 된 변수 사본을 가지고 있습니다."라고 매우 잘 설명합니다.
각 스레드에 자체 사본이 있어야 할 때 하나를 사용합니다. 기본적으로 데이터는 스레드간에 공유됩니다.
Webapp 서버는 스레드 풀을 유지할 수 ThreadLocal
있으며 클라이언트에 응답하기 전에 var를 제거해야하므로 다음 요청에서 현재 스레드를 다시 사용할 수 있습니다.
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();
}
}
여기서 withInitial 메서드는 람다 식을 사용하여 구현됩니다.
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)를 유지합니다. 가장 자주 사용되지만 스레드로부터 안전하지 않은 객체 중 하나는 데이터베이스 연결 및 JMSConnection입니다.
어떻게 ?
한 가지 예는 Spring 프레임 워크가 ThreadLocal 변수에 이러한 연결 객체를 유지함으로써 백그라운드에서 트랜잭션을 관리하기 위해 ThreadLocal을 많이 사용한다는 것입니다. 높은 수준에서 트랜잭션이 시작되면 연결을 얻고 자동 커밋을 비활성화하고 ThreadLocal에 유지합니다. 추가 db 호출에서 동일한 연결을 사용하여 db와 통신합니다. 마지막으로 ThreadLocal에서 연결을 가져와 트랜잭션을 커밋 (또는 롤백)하고 연결을 해제합니다.
log4j는 MDC를 유지하기 위해 ThreadLocal도 사용한다고 생각합니다.
ThreadLocal
다른 스레드간에 공유해서는 안되는 상태를 원하지만 전체 수명 동안 각 스레드에서 액세스 할 수 있어야 할 때 유용합니다.
예를 들어, 각 요청이 서로 다른 스레드에서 제공되는 웹 애플리케이션을 상상해보십시오. 각 요청에 대해 데이터 조각이 여러 번 필요하며 계산 비용이 많이 든다고 상상해보십시오. 그러나 해당 데이터는 들어오는 각 요청에 대해 변경되었을 수 있으므로 일반 캐시를 사용할 수 없습니다. 이 문제에 대한 간단하고 빠른 해결책은이 ThreadLocal
데이터에 대한 액세스 권한을 보유 하는 변수 를 갖는 것이므로 각 요청에 대해 한 번만 계산하면됩니다. 물론이 문제는를 사용하지 않고도 해결할 수 ThreadLocal
있지만, 저는 설명을 위해 고안했습니다.
즉, ThreadLocal
s는 본질적으로 글로벌 상태의 한 형태 라는 것을 명심하십시오 . 결과적으로 다른 많은 의미가 있으며 가능한 다른 모든 솔루션을 고려한 후에 만 사용해야합니다.
여기에 정말 새로운 것은 없지만 오늘 ThreadLocal
웹 애플리케이션에서 Bean Validation을 사용할 때 매우 유용 하다는 것을 발견했습니다 . 유효성 검사 메시지는 현지화되어 있지만 기본적으로 Locale.getDefault()
. Validator
다른으로 구성 할 수 MessageInterpolator
있지만 Locale
호출 할 때 를 지정할 수있는 방법은 없습니다 validate
. 따라서 정적 ThreadLocal<Locale>
(또는 더 좋은 방법은 필요한 다른 항목이 포함 된 일반 컨테이너를 ThreadLocal
만든 다음 사용자 지정에서 MessageInterpolator
선택하도록 Locale
할 수 있습니다. 다음 단계는 ServletFilter
세션 값을 사용 request.getLocale()
하는를 작성하거나 로캘을 선택하고 저장하는 것입니다. 그것은 당신에 ThreadLocal
대한 참조입니다.
As was mentioned by @unknown (google), it's usage is to define a global variable in which the value referenced can be unique in each thread. It's usages typically entails storing some sort of contextual information that is linked to the current thread of execution.
We use it in a Java EE environment to pass user identity to classes that are not Java EE aware (don't have access to HttpSession, or the EJB SessionContext). This way the code, which makes usage of identity for security based operations, can access the identity from anywhere, without having to explicitly pass it in every method call.
The request/response cycle of operations in most Java EE calls makes this type of usage easy since it gives well defined entry and exit points to set and unset the ThreadLocal.
ThreadLocal will ensure accessing the mutable object by the multiple threads in the non synchronized method is synchronized, means making the mutable object to be immutable within the method.
This is achieved by giving new instance of mutable object for each thread try accessing it. So It is local copy to the each thread. This is some hack on making instance variable in a method to be accessed like a local variable. As you aware method local variable is only available to the thread, one difference is; method local variables will not available to the thread once method execution is over where as mutable object shared with threadlocal will be available across multiple methods till we clean it up.
By Definition:
The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other's ThreadLocal variables.
Each Thread
in java contains ThreadLocalMap
in it.
Where
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
Achieving the ThreadLocal:
Now create a wrapper class for ThreadLocal which is going to hold the mutable object like below (with or without initialValue()
).
Now getter and setter of this wrapper will work on threadlocal instance instead of mutable object.
If getter() of threadlocal didn't find any value with in the threadlocalmap of the Thread
; then it will invoke the initialValue() to get its private copy with respect to the thread.
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();
}
}
Now wrapper.getDateFormatter()
will call threadlocal.get()
and that will check the currentThread.threadLocalMap
contains this (threadlocal) instance.
If yes return the value (SimpleDateFormat) for corresponding threadlocal instance
else add the map with this threadlocal instance, initialValue().
Herewith thread safety achieved on this mutable class; by each thread is working with its own mutable instance but with same ThreadLocal instance. Means All the thread will share the same ThreadLocal instance as key, but different SimpleDateFormat instance as value.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
Thread-local variables are often used to prevent sharing in designs based on mutable Singletons or global variables.
It can be used in scenarios like making seperate JDBC connection for each thread when you are not using a Connection Pool.
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
When you call getConnection, it will return a connection associated with that thread.The same can be done with other properties like dateformat, transaction context that you don't want to share between threads.
You could have also used local variables for the same, but these resource usually take up time in creation,so you don't want to create them again and again whenever you perform some business logic with them. However, ThreadLocal values are stored in the thread object itself and as soon as the thread is garbage collected, these values are gone too.
This link explains use of ThreadLocal very well.
The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other's ThreadLocal variables.
[For Reference]ThreadLocal cannot solve update problems of shared object. It is recommended to use a staticThreadLocal object which is shared by all operations in the same thread. [Mandatory]remove() method must be implemented by ThreadLocal variables, especially when using thread pools in which threads are often reused. Otherwise, it may affect subsequent business logic and cause unexpected problems such as memory leak.
Caching, sometime you have to calculate the same value lots of time so by storing the last set of inputs to a method and the result you can speed the code up. By using Thread Local Storage you avoid having to think about locking.
ThreadLocal is a specially provisioned functionality by JVM to provide an isolated storage space for threads only. like the value of instance scoped variable are bound to a given instance of a class only. each object has its only values and they can not see each other value. so is the concept of ThreadLocal variables, they are local to the thread in the sense of object instances other thread except for the one which created it, can not see it. See Here
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 provides a very easy way to achieve objects reusability with zero cost.
I had a situation where multiple threads were creating an image of mutable cache, on each update notification.
I used a Threadlocal on each thread, and then each thread would just need to reset old image and then update it again from the cache on each update notification.
Usual reusable objects from object pools have thread safety cost associated with them, while this approach has none.
There are 3 scenarios for using a class helper like SimpleDateFormat in multithread code, which best one is use ThreadLocal
Scenarios
1- Using like share object by the help of lock or synchronization mechanism which makes the app slow
2- Using as a local object inside a method
In this scenario, if we have 4 thread each one calls a method 1000 time then we have
4000 SimpleDateFormat object created and waiting for GC to erase them
3- Using ThreadLocal
if we have 4 thread and we gave to each thread one SimpleDateFormat instance
so we have 4 threads, 4 objects of SimpleDateFormat.
There is no need of lock mechanism and object creation and destruction. (Good time complexity and space complexity)
참고URL : https://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable
'Program Tip' 카테고리의 다른 글
JavaScript에서 나머지로 정수 나누기? (0) | 2020.09.28 |
---|---|
함수 설계 f (f (n)) == -n (0) | 2020.09.28 |
오류 : vcvarsall.bat를 찾을 수 없습니다. (0) | 2020.09.28 |
컬렉션이 수정되었습니다. (0) | 2020.09.28 |
자바 문자열을 날짜로 변환 (0) | 2020.09.28 |