Quando e come dovrei usare una variabile ThreadLocal?

Quando dovrei usare una variabile ThreadLocal ?

Come viene usato?

Un ansible (e comune) utilizzo è quando si ha un object che non è thread-safe, ma si vuole evitare di sincronizzare l’ accesso a quell’object (sto guardando voi, SimpleDateFormat ). Invece, assegna a ogni thread la propria istanza dell’object.

Per esempio:

 public class Foo { // SimpleDateFormat is not thread-safe, so give one to each thread private static final ThreadLocal formatter = new ThreadLocal(){ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); } } 

Documentazione

Poiché ThreadLocal è un riferimento ai dati all’interno di una determinata Thread , è ansible finire con il classloading delle perdite quando si utilizzano ThreadLocal nei server delle applicazioni che utilizzano i pool di thread. Devi stare molto attento a ripulire qualsiasi ThreadLocal che get() o set() usando il ThreadLocal remove() ThreadLocal .

Se non si esegue la pulizia una volta terminato, qualsiasi riferimento contenuto nelle classi caricate come parte di un’applicazione Web distribuita rimarrà nell’heap permanente e non verrà mai raccolto. La ridistribuzione / annullamento della webapp non ripulirà il riferimento di ogni Thread alle classi del tuo webapp poiché la Thread non è qualcosa di proprietà della tua webapp. Ogni distribuzione successiva creerà una nuova istanza della class che non sarà mai raccolta.

java.lang.OutOfMemoryError: PermGen space con delle eccezioni di memoria a causa di java.lang.OutOfMemoryError: PermGen space e dopo qualche googling probabilmente aumenterai -XX:MaxPermSize invece di correggere il bug.

Se si verificano questi problemi, è ansible determinare quale thread e class mantengono questi riferimenti utilizzando Memory Analyzer di Eclipse e / o seguendo la guida e il followup di Frank Kieviet .

Aggiornamento: riscoperto il post di Alex Vasseur che mi ha aiutato a rintracciare alcuni problemi ThreadLocal che stavo avendo.

Molti framework utilizzano ThreadLocals per mantenere un contesto correlato al thread corrente. Ad esempio, quando la transazione corrente è memorizzata in un ThreadLocal, non è necessario passarlo come parametro attraverso ogni chiamata al metodo, nel caso in cui qualcuno in fondo alla pila abbia bisogno di accedervi. Le applicazioni Web possono archiviare informazioni sulla richiesta corrente e sulla sessione in un ThreadLocal, in modo che l’applicazione possa accedervi facilmente. Con Guice è ansible utilizzare ThreadLocals quando si implementano ambiti personalizzati per gli oggetti inseriti (molto probabilmente gli ambiti di servlet di Guice vengono utilizzati anche).

ThreadLocals è una sorta di variabili globali (anche se un po ‘meno malvagie perché sono limitate a un thread), quindi dovresti fare attenzione quando li usi per evitare effetti collaterali indesiderati e perdite di memoria. Progetta le tue API in modo che i valori ThreadLocal vengano sempre cancellati automaticamente quando non sono più necessari e che l’uso non corretto dell’API non sarà ansible (ad esempio in questo modo ). ThreadLocals può essere utilizzato per rendere il codice più pulito e, in alcuni rari casi, è l’unico modo per far funzionare qualcosa (il mio progetto corrente aveva due casi simili: sono documentati qui sotto “Campi statici e variabili globali”).

In Java, se si dispone di un dato che può variare per thread, le proprie scelte sono di passare quel dato a ogni metodo che necessita (o potrebbe averne bisogno) o di associare il dato al thread. Passare il dato ovunque può essere praticabile se tutti i metodi già hanno bisogno di passare attorno a una variabile “contesto” comune.

In caso contrario, potresti non voler ingombrare le firme dei metodi con un parametro aggiuntivo. In un mondo senza thread, è ansible risolvere il problema con l’equivalente Java di una variabile globale. In una parola con thread, l’equivalente di una variabile globale è una variabile locale del thread.

Essenzialmente, quando hai bisogno del valore di una variabile per dipendere dal thread corrente e non è conveniente per te colbind il valore al thread in qualche altro modo (per esempio, sottoclass di thread).

Un caso tipico è dove alcuni altri framework hanno creato il thread in cui è in esecuzione il codice, ad esempio un servlet container, o dove ha più senso usare ThreadLocal perché la variabile è quindi “nella sua posizione logica” (piuttosto che una variabile appeso a una sottoclass di Thread o in qualche altra mappa di hash).

Sul mio sito web, ho qualche ulteriore discussione ed esempi su quando utilizzare ThreadLocal che potrebbe essere di interesse.

Alcune persone sostengono l’uso di ThreadLocal come un modo per colbind un “ID thread” a ciascun thread in determinati algoritmi concorrenti in cui è necessario un numero di thread (vedere ad esempio Herlihy & Shavit). In questi casi, verifica di avere davvero un vantaggio!

C’è un ottimo esempio in Book Java Concurrency in Practice . Dove l’autore ( Joshua Bloch ) spiega come il confinamento del filo è uno dei modi più semplici per ottenere la sicurezza del filo e ThreadLocal è un mezzo più formale per mantenere il confinamento del filo. Alla fine spiega anche come le persone possono abusarne usando come variabili globali.

Ho copiato il testo dal libro menzionato ma manca il codice 3.10 in quanto non è molto importante capire dove dovrebbe essere usato ThreadLocal.

Le variabili locali del thread sono spesso utilizzate per impedire la condivisione di progetti basati su Singletons mutabili o variabili globali. Ad esempio, un’applicazione a thread singolo potrebbe mantenere una connessione di database globale inizializzata all’avvio per evitare di dover passare una connessione a ogni metodo. Poiché le connessioni JDBC potrebbero non essere thread-safe, un’applicazione multithread che utilizza una connessione globale senza coordinamento aggiuntivo non è thread-safe. Usando un ThreadLocal per memorizzare la connessione JDBC, come in ConnectionHolder nel Listato 3.10, ogni thread avrà la propria connessione.

ThreadLocal è ampiamente utilizzato nell’implementazione di framework applicativi. Ad esempio, i contenitori J2EE associano un contesto di transazione a un thread in esecuzione per la durata di una chiamata EJB. Questo è facilmente implementabile usando un thread-Locale statico che tiene il contesto della transazione: quando il codice del framework deve determinare quale transazione è attualmente in esecuzione, recupera il contesto della transazione da questo ThreadLocal. Ciò è conveniente in quanto riduce la necessità di passare informazioni di contesto di esecuzione in ogni metodo, ma accoppia qualsiasi codice che utilizza questo meccanismo al framework.

È facile abusare di ThreadLocal trattando la proprietà del confinamento del thread come licenza per utilizzare variabili globali o come mezzo per creare argomenti del metodo “nascosti”. Come le variabili globali, le variabili locali del thread possono sminuire la riusabilità e introdurre accoppiamenti nascosti tra le classi, e dovrebbero quindi essere utilizzate con attenzione.

La documentazione lo dice molto bene: “ogni thread che accede a [una variabile locale del thread] (tramite il suo metodo get o set) ha una propria copia inizializzata indipendente della variabile”.

Ne usi uno quando ogni thread deve avere la sua copia di qualcosa. Per impostazione predefinita, i dati sono condivisi tra thread.

Il server Webapp può mantenere un pool di thread e una ThreadLocal deve essere rimossa prima della risposta al client, pertanto il thread corrente può essere riutilizzato dalla richiesta successiva.

  1. ThreadLocal in Java era stato introdotto su JDK 1.2, ma in seguito è stato generato in JDK 1.5 per introdurre la sicurezza del tipo sulla variabile ThreadLocal.

  2. ThreadLocal può essere associato all’ambito Thread, tutto il codice che viene eseguito da Thread ha accesso alle variabili ThreadLocal ma due thread non possono vedere le altre variabili ThreadLocal.

  3. Ogni thread contiene una copia esclusiva della variabile ThreadLocal che diventa idonea alla raccolta Garbage dopo che il thread è terminato o è morto, normalmente oa causa di un’eccezione. Dato che la variabile ThreadLocal non ha altri riferimenti live.

  4. Le variabili ThreadLocal in Java sono generalmente campi statici privati ​​in Classi e mantengono il suo stato all’interno di Thread.

Per saperne di più: http://javarevisited.blogspot.com/2012/05/how-to-use-threadlocal-in-java-benefits.html#ixzz2XltgbHTK

Due casi d’uso in cui è ansible utilizzare la variabile threadlocal –
1- Quando abbiamo il requisito di associare lo stato a un thread (ad esempio, un ID utente o ID transazione). Questo di solito accade con un’applicazione web che ogni richiesta diretta a un servlet ha un ID transazione univoco ad esso associato.

 // 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 threadId = ThreadLocal.withInitial(()-> {return nextId.getAndIncrement();}); // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } } 

Nota che qui il metodo conInitial è implementato usando l’espressione lambda.
2- Un altro caso d’uso è quando vogliamo avere un’istanza thread-safe e non vogliamo usare la sincronizzazione poiché il costo delle prestazioni con la sincronizzazione è maggiore. Uno di questi casi è quando viene utilizzato SimpleDateFormat. Poiché SimpleDateFormat non è thread-safe, dobbiamo fornire un meccanismo per renderlo thread-safe.

 public class ThreadLocalDemo1 implements Runnable { // threadlocal variable is created private static final ThreadLocal dateFormat = new ThreadLocal(){ @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())); } } 

Devi stare molto attento con il pattern ThreadLocal. Ci sono alcuni aspetti negativi come Phil menzionato, ma uno che non è stato menzionato è quello di assicurarsi che il codice che imposta il contesto ThreadLocal non sia “ri-entrante”.

Le cose brutte possono accadere quando il codice che imposta le informazioni viene eseguito una seconda o terza volta, poiché le informazioni sul thread possono iniziare a mutare quando non te l’aspettavi. Quindi, accertarsi che le informazioni ThreadLocal non siano state impostate prima di reimpostarle.

Niente di veramente nuovo qui, ma ho scoperto oggi che ThreadLocal è molto utile quando si utilizza la convalida del bean in un’applicazione web. I messaggi di convalida sono localizzati, ma per impostazione predefinita utilizzare Locale.getDefault() . È ansible configurare il Validator con un diverso MessageInterpolator , ma non è ansible specificare l’ Locale quando si chiama validate . Quindi è ansible creare un ThreadLocal statico (o, meglio ancora, un contenitore generale con altre cose che potrebbero essere necessarie per ThreadLocal e quindi fare in modo che il proprio MessageInterpolator personalizzato scelga l’ ServletFilter . Il passo successivo è scrivere un ServletFilter che utilizza una sessione value o request.getLocale() per selezionare la locale e memorizzarla nel riferimento ThreadLocal .

Come menzionato da @unknown (google), il suo utilizzo è definire una variabile globale in cui il valore referenziato può essere univoco in ogni thread. In genere, gli usi comportano la memorizzazione di una sorta di informazioni contestuali collegate al thread di esecuzione corrente.

Lo usiamo in un ambiente Java EE per passare l’id quadro dell’utente a classi che non sono a conoscenza di Java EE (non si ha accesso a HttpSession o a EJB SessionContext). In questo modo il codice, che rende ansible l’utilizzo dell’id quadro per operazioni basate sulla sicurezza, può accedere all’id quadro da qualsiasi luogo, senza doverlo passare esplicitamente in ogni chiamata di metodo.

Il ciclo di richiesta / risposta delle operazioni nella maggior parte delle chiamate Java EE rende questo tipo di utilizzo facile poiché fornisce punti di ingresso e uscita ben definiti per impostare e distriggersre il ThreadLocal.

ThreadLocal garantirà l’accesso all’object mutabile da più thread nel metodo non sincronizzato è sincronizzato, significa rendere l’object mutabile per essere immutabile all’interno del metodo.

Ciò si ottiene dando una nuova istanza di object mutabile per ogni thread prova ad accedervi. Quindi è una copia locale per ogni thread. Questo è un modo per modificare la variabile di istanza in un metodo a cui accedere come una variabile locale. Come sai, la variabile locale del metodo è disponibile solo per il thread, una differenza è; Le variabili locali del metodo non saranno disponibili per il thread una volta che l’esecuzione del metodo è finita, in cui l’object mutabile condiviso con threadlocal sarà disponibile su più metodi fino a quando non lo puliremo.

Per definizione:

La class ThreadLocal in Java consente di creare variabili che possono essere solo lette e scritte dallo stesso thread. Pertanto, anche se due thread eseguono lo stesso codice e il codice ha un riferimento a una variabile ThreadLocal, i due thread non possono vedere le variabili ThreadLocal degli altri.

Ogni Thread in java contiene ThreadLocalMap in esso.
Dove

 Key = One ThreadLocal object shared across threads. value = Mutable object which has to be used synchronously, this will be instantiated for each thread. 

Raggiungere il ThreadLocal:

Ora crea una class wrapper per ThreadLocal che terrà l’object mutabile come sotto (con o senza initialValue() ).
Ora getter e setter di questo wrapper funzioneranno su istanze threadlocal invece che su oggetti mutabili.

Se getter () di threadlocal non ha trovato alcun valore con nella threadlocalmap del Thread ; quindi invocherà initialValue () per ottenere la sua copia privata rispetto al thread.

 class SimpleDateFormatInstancePerThread { private static final ThreadLocal dateFormatHolder = new ThreadLocal() { @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(); } } 

Ora wrapper.getDateFormatter() chiamerà threadlocal.get() e controllerà che currentThread.threadLocalMap contenga questa istanza (threadlocal).
Se si restituisce il valore (SimpleDateFormat) per l’istanza threadlocal corrispondente
altrimenti aggiungi la mappa con questa istanza threadlocal, initialValue ().

Con la presente sicurezza del filo ottenuta su questa class mutevole; da ogni thread sta lavorando con la propria istanza mutabile ma con la stessa istanza ThreadLocal. Mezzi Tutti i thread condivideranno la stessa istanza ThreadLocal come chiave, ma diversa istanza SimpleDateFormat come valore.

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java

quando?

Quando un object non è thread-safe, anziché la sincronizzazione che ostacola la scalabilità, assegnare un object a ogni thread e mantenerlo nell’ambito del thread, che è ThreadLocal. Uno degli oggetti utilizzati più spesso ma non thread-safe sono Database Connection e JMSConnection.

Come ?

Un esempio è il framework Spring che utilizza pesantemente ThreadLocal per gestire le transazioni dietro le quinte mantenendo questi oggetti di connessione nelle variabili ThreadLocal. A livello alto, quando viene avviata una transazione, ottiene la connessione (e disabilita il commit automatico) e la mantiene in ThreadLocal. su ulteriori chiamate db usa la stessa connessione per comunicare con db. Alla fine, prende la connessione da ThreadLocal e commette (o rollback) la transazione e rilascia la connessione.

Penso che log4j usi anche ThreadLocal per mantenere MDC.

ThreadLocal è utile quando vuoi avere uno stato che non dovrebbe essere condiviso tra thread diversi, ma dovrebbe essere accessibile da ogni thread durante la sua intera vita.

Ad esempio, immagina un’applicazione web, in cui ogni richiesta è servita da un thread diverso. Immagina che per ogni richiesta hai bisogno di più dati, il che è piuttosto costoso da calcolare. Tuttavia, tali dati potrebbero essere cambiati per ogni richiesta in entrata, il che significa che non è ansible utilizzare una cache normale. Una soluzione semplice e rapida a questo problema sarebbe avere una variabile ThreadLocal in grado di accedere a questi dati, in modo che sia necessario calcolarla solo una volta per ogni richiesta. Ovviamente, questo problema può anche essere risolto senza l’uso di ThreadLocal , ma l’ho ideato a scopo illustrativo.

Detto questo, tenete presente che i ThreadLocal sono essenzialmente una forma di stato globale. Di conseguenza, ha molte altre implicazioni e dovrebbe essere utilizzato solo dopo aver considerato tutte le altre possibili soluzioni.

Le variabili locali del thread sono spesso utilizzate per impedire la condivisione di progetti basati su Singletons mutabili o variabili globali.

Può essere utilizzato in scenari come la creazione di una connessione JDBC separata per ogni thread quando non si utilizza un pool di connessioni.

 private static ThreadLocal connectionHolder = new ThreadLocal() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); } 

Quando chiami getConnection, verrà restituita una connessione associata a quel thread. Lo stesso può essere fatto con altre proprietà come dateformat, contesto di transazione che non vuoi condividere tra thread.

Potresti anche aver usato le variabili locali per lo stesso, ma di solito queste risorse impiegano del tempo nella creazione, quindi non vuoi crearle più e più volte ogni volta che esegui delle logiche di business con loro. Tuttavia, i valori ThreadLocal vengono memorizzati nell’object thread stesso e, non appena il thread viene raccolto, questi valori vengono eliminati.

Questo link spiega l’uso di ThreadLocal molto bene.

Dal rilascio di Java 8, c’è un modo più dichiarativo per inizializzare ThreadLocal :

 ThreadLocal local = ThreadLocal.withInitial(() -> "init value"); 

Fino al rilascio di Java 8 dovevi fare quanto segue:

 ThreadLocal local = new ThreadLocal(){ @Override protected String initialValue() { return "init value"; } }; 

Inoltre, se il metodo di istanziazione (costruttore, metodo factory) della class che viene utilizzato per ThreadLocal non prende alcun parametro, puoi semplicemente utilizzare i riferimenti al metodo (introdotto in Java 8):

 class NotThreadSafe { // no parameters public NotThreadSafe(){} } ThreadLocal container = ThreadLocal.withInitial(NotThreadSafe::new); 

Nota: la valutazione è lenta poiché si passa a java.util.function.Supplier lambda del programma di valutazione viene valutato solo quando viene chiamato ThreadLocal#get ma il valore non è stato precedentemente valutato.

Caching, a volte è necessario calcolare lo stesso valore un sacco di tempo in modo da memorizzare l’ultimo set di input per un metodo e il risultato è ansible accelerare il codice. Utilizzando Thread Local Storage eviti di dover pensare al blocco.

ThreadLocal è una funzionalità appositamente predisposta da JVM per fornire uno spazio di archiviazione isolato solo per i thread. come il valore della variabile scope dell’istanza sono associati a una determinata istanza di una class. ogni object ha i suoi soli valori e non possono vedere l’un l’altro valore. così è il concetto di variabili ThreadLocal, sono locali al thread nel senso di istanze dell’object altro thread tranne per quello che lo ha creato, non può vederlo. Vedere qui

 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 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 fornisce un modo molto semplice per ottenere la riusabilità degli oggetti a costo zero.

Ho avuto una situazione in cui più thread stavano creando un’immagine di cache mutabile, su ogni notifica di aggiornamento.

Ho usato un threadlocal su ogni thread e quindi ogni thread avrebbe solo bisogno di ripristinare la vecchia immagine e quindi aggiornarla di nuovo dalla cache su ogni notifica di aggiornamento.

Gli oggetti riutilizzabili usuali dai pool di oggetti hanno un costo di sicurezza dei thread ad essi associato, mentre questo approccio non ne ha.

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.

Leggi di più