Quando dovrei usare una ThreadLocal
variabile?
Come si usa
RequestUtils.getCurrentRequestThreadLocal()
. Non dire questo è molto elegante, ma ciò deriva dal fatto che ThreadLocal stesso non è molto elegante nella maggior parte dei casi.
Quando dovrei usare una ThreadLocal
variabile?
Come si usa
RequestUtils.getCurrentRequestThreadLocal()
. Non dire questo è molto elegante, ma ciò deriva dal fatto che ThreadLocal stesso non è molto elegante nella maggior parte dei casi.
Risposte:
Un uso possibile (e comune) è quando hai qualche oggetto che non è thread-safe, ma vuoi evitare di sincronizzare l' accesso a quell'oggetto (ti sto guardando, SimpleDateFormat ). Invece, assegna a ogni thread la propria istanza dell'oggetto.
Per esempio:
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
. Forse è meglio usare un'alternativa thread-safe . Se sei d'accordo che i singoli sono cattivi, allora ThreadLocal
è anche peggio.
Poiché a ThreadLocal
è un riferimento ai dati all'interno di un dato Thread
, è possibile finire con perdite di caricamento di classe quando si usano ThreadLocal
s nei server delle applicazioni usando pool di thread. È necessario essere molto attenti a ripulire eventuali ThreadLocal
s voi get()
o set()
utilizzando la ThreadLocal
's remove()
metodo.
Se non si esegue la pulizia al termine, tutti i riferimenti che contiene alle classi caricate come parte di una webapp distribuita rimarranno nell'heap permanente e non verranno mai raccolti rifiuti. La ridistribuzione / non distribuzione della webapp non pulirà il Thread
riferimento di ciascuna alla classe o alle classi della tua webapp poiché Thread
non è qualcosa di proprietà della tua webapp. Ogni successiva distribuzione creerà una nuova istanza della classe che non verrà mai raccolta in modo inutile.
Si finiranno con eccezioni di memoria insufficiente a causa java.lang.OutOfMemoryError: PermGen space
e dopo che alcuni googling probabilmente aumenteranno -XX:MaxPermSize
invece di correggere il bug.
Se alla fine si verificano questi problemi, è possibile determinare quale thread e classe trattiene questi riferimenti utilizzando l'analizzatore di memoria di Eclipse e / o seguendo la guida e il follow - up di Frank Kieviet .
Aggiornamento: ho riscoperto il blog di Alex Vasseur che mi ha aiutato a rintracciare alcuni ThreadLocal
problemi che stavo riscontrando .
Molti framework utilizzano ThreadLocals per mantenere un contesto correlato al thread corrente. Ad esempio, quando la transazione corrente viene archiviata in una ThreadLocal, non è necessario passarla come parametro attraverso ogni chiamata di metodo, nel caso in cui qualcuno in basso nello stack abbia bisogno di accedervi. Le applicazioni Web potrebbero archiviare informazioni sulla richiesta e sulla sessione correnti in un ThreadLocal, in modo che l'applicazione possa accedervi facilmente. Con Guice puoi usare ThreadLocals quando implementi ambiti personalizzati per gli oggetti iniettati ( molto probabilmente anche gli ambiti servlet predefiniti di Guice li usano).
ThreadLocals è una sorta di variabili globali (anche se leggermente meno male perché sono limitate a un thread), quindi dovresti stare attento 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 non sarà possibile un uso errato dell'API (ad esempio come questo ). ThreadLocals può essere usato per rendere il codice più pulito e in alcuni rari casi è l'unico modo per far funzionare qualcosa (il mio progetto attuale ha avuto due casi del genere; sono documentati qui sotto "Campi statici e variabili globali").
In Java, se si dispone di un dato che può variare per thread, è possibile passare tale dato a tutti i metodi che ne necessitano (o potrebbero averne bisogno) o associare il dato al thread. Il passaggio del dato ovunque può essere praticabile se tutti i metodi devono già passare attorno a una variabile "contesto" comune.
In caso contrario, potresti non voler ingombrare le firme del metodo con un parametro aggiuntivo. In un mondo senza thread, è possibile risolvere il problema con l'equivalente Java di una variabile globale. In una parola thread, l'equivalente di una variabile globale è una variabile thread-local.
'datum' is the singular form and 'data' is the plural form.
C'è un ottimo esempio nel libro Java Concurrency in Practice . Laddove l'autore ( Joshua Bloch ) spiega come il confinamento del thread sia uno dei modi più semplici per ottenere la sicurezza del thread e ThreadLocal è il mezzo più formale per mantenere il confinamento del thread. Alla fine spiega anche come le persone possono abusarne usandolo come variabili globali.
Ho copiato il testo dal libro citato ma manca il codice 3.10 in quanto non è molto importante capire dove utilizzare ThreadLocal.
Le variabili thread-local sono spesso utilizzate per impedire la condivisione in progetti basati su Singleton mutabili o variabili globali. Ad esempio, un'applicazione a thread singolo potrebbe mantenere una connessione al database globale inizializzata all'avvio per evitare di dover passare una connessione a tutti i metodi. Poiché le connessioni JDBC potrebbero non essere thread-safe, un'applicazione multithread che utilizza una connessione globale senza coordinamento aggiuntivo non è neanche 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-Local statico che contiene 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ò è utile in quanto riduce la necessità di passare le informazioni sul contesto di esecuzione in ogni metodo, ma accoppia qualsiasi codice che utilizza questo meccanismo al framework.
È facile abusare di ThreadLocal trattando la sua proprietà di confinamento del thread come una licenza per usare variabili globali o come mezzo per creare argomenti di metodo "nascosti". Come le variabili globali, le variabili thread-local possono sminuire la riusabilità e introdurre accoppiamenti nascosti tra le classi e dovrebbero quindi essere usate con cura.
In sostanza, quando è necessario il valore di una variabile per dipendere dal thread corrente e non è conveniente allegare il valore al thread in altro modo (ad esempio, thread di sottoclasse).
Un caso tipico è quello in cui un altro framework ha creato il thread in cui è in esecuzione il codice, ad esempio un contenitore servlet, o in cui ha più senso utilizzare ThreadLocal perché la variabile è quindi "al suo posto logico" (piuttosto che una variabile pendente da una sottoclasse di thread o in un'altra mappa hash).
Sul mio sito web, ho ulteriori discussioni ed esempi su quando usare ThreadLocal che potrebbero anche essere di interesse.
Alcune persone sostengono l'utilizzo di ThreadLocal come metodo per allegare un "ID thread" a ciascun thread in determinati algoritmi simultanei in cui è necessario un numero di thread (vedere ad esempio Herlihy & Shavit). In questi casi, controlla che stai davvero ottenendo un vantaggio!
ThreadLocal in Java era stato introdotto su JDK 1.2 ma successivamente è stato generato in JDK 1.5 per introdurre la sicurezza del tipo sulla variabile ThreadLocal.
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 l'un l'altro la variabile ThreadLocal.
Ogni thread contiene una copia esclusiva della variabile ThreadLocal che diventa ammissibile alla Garbage Collection dopo che il thread è terminato o è morto, normalmente o a causa di un'eccezione, dato che la variabile ThreadLocal non ha altri riferimenti attivi.
Le variabili ThreadLocal in Java sono generalmente campi statici privati nelle Classi e mantengono il suo stato all'interno di Thread.
Ulteriori informazioni: ThreadLocal in Java - Esempio di programma ed esercitazione
La documentazione lo dice molto bene: "ogni thread che accede a [una variabile thread-local] (tramite il suo metodo get o set) ha la sua copia della variabile, inizializzata in modo indipendente".
Ne usi uno quando ogni thread deve avere la propria copia di qualcosa. Per impostazione predefinita, i dati sono condivisi tra i thread.
Due casi d'uso in cui è possibile utilizzare la variabile threadlocal -
1- Quando è necessario associare lo stato a un thread (ad esempio, un ID utente o un ID transazione). Ciò accade in genere con un'applicazione Web che a ogni richiesta indirizzata a un servlet è associato un ID transazione univoco.
// 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();
}
}
Si noti che qui il metodo con iniziale è 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 si utilizza SimpleDateFormat. Poiché SimpleDateFormat non è thread-safe, quindi dobbiamo fornire un meccanismo per renderlo thread-safe.
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()));
}
}
Dalla versione Java 8, esiste un modo più dichiarativo per inizializzare ThreadLocal
:
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
Fino alla versione Java 8 dovevi fare quanto segue:
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
Inoltre, se il metodo di istanza (costruttore, metodo factory) della classe utilizzata ThreadLocal
non accetta alcun parametro, è possibile utilizzare semplicemente i riferimenti al metodo (introdotti in Java 8):
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
Nota: la
valutazione è lenta poiché si passa java.util.function.Supplier
lambda che viene valutata solo quando ThreadLocal#get
viene chiamata ma il valore non è stato precedentemente valutato.
Devi stare molto attento con il modello ThreadLocal. Ci sono alcuni lati negativi importanti come Phil menzionato, ma uno che non è stato menzionato è quello di assicurarsi che il codice che imposta il contesto ThreadLocal non sia "rientrante".
Cose brutte possono accadere quando il codice che imposta le informazioni viene eseguito una seconda o terza volta perché le informazioni sul tuo thread possono iniziare a mutare quando non te l'aspettavi. Quindi assicurati di non aver impostato le informazioni ThreadLocal prima di impostarle di nuovo.
ThreadLocal
schema. Se lo fai F(){ member=random(); F2(); write(member); }
e F2 sostituisce il membro con un nuovo valore, ovviamente write(member)
non scriverà più il numero che hai random()
ed. Questo è letteralmente solo buon senso. Allo stesso modo, se lo fai F(){ F(); }
, allora buona fortuna con il tuo loop infinito! Questo è vero ovunque e non è specifico ThreadLocal
.
quando?
Quando un oggetto non è thread-safe, invece della sincronizzazione che ostacola la scalabilità, assegnare un oggetto a ogni thread e mantenerlo nell'ambito del thread, ovvero ThreadLocal. Uno degli oggetti più usati ma non thread-safe è Database Connection e JMSConnection.
Come ?
Un esempio è il framework Spring che utilizza ThreadLocal pesantemente per gestire le transazioni dietro le quinte mantenendo questi oggetti di connessione nelle variabili ThreadLocal. Ad alto livello, quando una transazione viene avviata ottiene la connessione (e disabilita il commit automatico) e la mantiene in ThreadLocal. su ulteriori chiamate db utilizza la stessa connessione per comunicare con db. Alla fine, prende la connessione da ThreadLocal e commette (o ripristina) la transazione e rilascia la connessione.
Penso che log4j usi anche ThreadLocal per mantenere MDC.
ThreadLocal
è utile quando si desidera avere uno stato che non deve essere condiviso tra thread diversi, ma deve 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 sia necessario più volte un pezzo di dati, che è piuttosto costoso da calcolare. Tuttavia, tali dati potrebbero essere cambiati per ogni richiesta in arrivo, il che significa che non è possibile utilizzare una cache semplice. Una soluzione semplice e rapida a questo problema sarebbe quella di avere una ThreadLocal
variabile che mantiene l'accesso a questi dati, in modo da doverli calcolare solo una volta per ogni richiesta. Naturalmente, questo problema può anche essere risolto senza l'uso di ThreadLocal
, ma l'ho ideato a scopo illustrativo.
Detto questo, tieni presente che ThreadLocal
s sono essenzialmente una forma di stato globale. Di conseguenza, ha molte altre implicazioni e dovrebbe essere usato solo dopo aver considerato tutte le altre possibili soluzioni.
ThreadLocal
come campo oggetto ...
ThreadLocal
, dove era accessibile a livello globale. Come hai detto, può ancora essere usato come un campo di classe che limita la visibilità.
Niente di veramente nuovo qui, ma ho scoperto oggi che ThreadLocal
è molto utile quando si utilizza Bean Validation in un'applicazione Web. I messaggi di convalida sono localizzati, ma per impostazione predefinita Locale.getDefault()
. È possibile configurare il Validator
con un diverso MessageInterpolator
, ma non è possibile specificare il Locale
quando si chiama validate
. Quindi potresti creare un ThreadLocal<Locale>
contenitore statico (o meglio ancora, un contenitore generale con altre cose che potresti essere necessario ThreadLocal
e quindi avere la tua MessageInterpolator
scelta personalizzata Locale
da quella. Il passo successivo è scrivere un ServletFilter
che utilizza un valore di sessione o request.getLocale()
scegliere la locale e l'archivio nel tuo ThreadLocal
riferimento.
Come accennato da @unknown (google), il suo utilizzo è definire una variabile globale in cui il valore a cui si fa riferimento può essere univoco in ogni thread. I suoi usi in genere comportano l'archiviazione di una sorta di informazione contestuale collegata all'attuale thread di esecuzione.
Lo usiamo in un ambiente Java EE per trasmettere l'identità dell'utente a classi che non sono consapevoli di Java EE (non hanno accesso a HttpSession o SJECessionxt). In questo modo il codice, che fa uso dell'identità per operazioni basate sulla sicurezza, può accedere all'identità da qualsiasi luogo, senza doverlo passare esplicitamente in ogni chiamata di metodo.
Il ciclo di richiesta / risposta delle operazioni nella maggior parte delle chiamate EE Java semplifica questo tipo di utilizzo poiché fornisce punti di entrata e uscita ben definiti per impostare e disinserire ThreadLocal.
ThreadLocal assicurerà che l'accesso all'oggetto mutabile da parte di più thread nel metodo non sincronizzato sia sincronizzato, il che significa rendere l'oggetto mutabile immutabile all'interno del metodo.
Ciò si ottiene dando una nuova istanza di oggetto mutabile per ogni thread che prova ad accedervi. Quindi è una copia locale per ogni thread. Questo è un trucco per rendere variabile l'istanza in un metodo a cui si accede come una variabile locale. Come sapete, 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 terminata l'esecuzione del metodo, poiché l'oggetto mutabile condiviso con threadlocal sarà disponibile su più metodi fino a quando non verrà pulito.
Per definizione:
La classe ThreadLocal in Java consente di creare variabili che possono essere lette e scritte solo 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 reciproche.
Ciascuno Thread
in Java contiene ThreadLocalMap
al suo interno.
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 classe wrapper per ThreadLocal che conterrà l'oggetto mutabile come sotto (con o senza initialValue()
).
Ora getter e setter di questo wrapper funzioneranno sull'istanza threadlocal invece che sull'oggetto mutabile.
Se getter () di threadlocal non ha trovato alcun valore con in threadlocalmap di Thread
; quindi invocherà initialValue () per ottenere la sua copia privata rispetto al 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();
}
}
Adesso wrapper.getDateFormatter()
chiamerà threadlocal.get()
e controllerà che currentThread.threadLocalMap
contiene questa istanza (threadlocal).
Se sì, restituisce il valore (SimpleDateFormat) per l'istanza threadlocal corrispondente,
altrimenti aggiungi la mappa con questa istanza threadlocal, initialValue ().
Con la presente sicurezza del thread raggiunta su questa classe mutabile; da ogni thread sta lavorando con la propria istanza mutabile ma con la stessa istanza ThreadLocal. Mezzi Tutto il thread condividerà la stessa istanza ThreadLocal della chiave, ma diversa istanza SimpleDateFormat come valore.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
Le variabili thread-local sono spesso utilizzate per impedire la condivisione in progetti basati su Singleton mutabili o variabili globali.
Può essere utilizzato in scenari come la connessione JDBC separata per ogni thread quando non si utilizza un pool di connessioni.
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
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 desideri condividere tra i thread.
Avresti potuto anche usare le variabili locali per lo stesso, ma queste risorse di solito impiegano molto tempo nella creazione, quindi non vuoi crearle più e più volte ogni volta che esegui una logica aziendale con loro. Tuttavia, i valori ThreadLocal vengono archiviati nell'oggetto thread stesso e non appena il thread viene garbage collection, anche questi valori scompaiono.
Questo link spiega molto bene l'uso di ThreadLocal.
La classe ThreadLocal in Java consente di creare variabili che possono essere lette e scritte solo 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 reciproche.
Esistono 3 scenari per l'utilizzo di un helper di classe come SimpleDateFormat nel codice multithread, il migliore dei quali è utilizzare ThreadLocal
scenari
1- Utilizzare like share object con l'aiuto del meccanismo di blocco o sincronizzazione che rallenta l'app
2- Utilizzo come oggetto locale all'interno di un metodo
In questo scenario, se abbiamo 4 thread ognuno chiama un metodo 1000 volte, allora abbiamo
4000 oggetti SimpleDateFormat creati e in attesa che GC li cancelli
3- Utilizzo di ThreadLocal
se abbiamo 4 thread e abbiamo dato a ogni thread un'istanza SimpleDateFormat,
quindi abbiamo 4 thread , 4 oggetti di SimpleDateFormat.
Non sono necessari meccanismi di blocco, creazione e distruzione di oggetti. (Buona complessità temporale e complessità spaziale)
[Per riferimento] ThreadLocal non può risolvere i problemi di aggiornamento dell'oggetto condiviso. Si consiglia di utilizzare un oggetto staticThreadLocal che è condiviso da tutte le operazioni nello stesso thread. Il metodo [Obbligatorio] remove () deve essere implementato dalle variabili ThreadLocal, specialmente quando si utilizzano pool di thread in cui i thread vengono spesso riutilizzati. Altrimenti, potrebbe influire sulla logica aziendale successiva e causare problemi imprevisti come perdita di memoria.
La memorizzazione nella cache, a volte è necessario calcolare lo stesso valore per un sacco di tempo, quindi memorizzando l'ultimo set di input in un metodo e il risultato è possibile 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 con ambito istanza sono associati solo a una determinata istanza di una classe. ogni oggetto ha i suoi unici valori e non possono vedersi a vicenda. così è il concetto di variabili ThreadLocal, sono locali al thread nel senso di istanze di oggetto altri thread ad eccezione di quello che lo ha creato, non possono 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<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 offre 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, ad ogni notifica di aggiornamento.
Ho usato un Threadlocal su ogni thread, quindi ogni thread avrebbe solo bisogno di ripristinare la vecchia immagine e quindi aggiornarlo di nuovo dalla cache su ogni notifica di aggiornamento.
Agli oggetti riutilizzabili usuali dai pool di oggetti è associato un costo di sicurezza del thread, mentre questo approccio non ne ha.
Prova questo piccolo esempio, per avere un'idea della variabile 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();
}
}
Output della console, se il thread BookA viene eseguito per primo:
Risultato BookA: 'wordA1, wordA2, wordA3'.
Libro dei risultati B: 'wordB1, wordB2'.
Output della console, se il thread BookB viene eseguito per primo:
risultato BookB: 'wordB1, wordB2'.
Libro dei risultati A: 'wordA1, wordA2, wordA3'.