Quando e come dovrei usare una variabile ThreadLocal?


877

Quando dovrei usare una ThreadLocalvariabile?

Come si usa


11
Se stai usando ThreadLocal, non scrivere mai un wrapper su di esso !!! Ogni sviluppatore che desidera utilizzare la variabile deve sapere che è "ThreadLocal"
Non un bug,

2
@Notabug Se sei esplicito, puoi nominare la tua variabile in modo tale che tu abbia a che fare con un valore ThreadLocal, ad es RequestUtils.getCurrentRequestThreadLocal(). Non dire questo è molto elegante, ma ciò deriva dal fatto che ThreadLocal stesso non è molto elegante nella maggior parte dei casi.
Whirlwin,

@Sasha È possibile utilizzare ThreadLocal per memorizzare la traccia dell'esecuzione
gaurav,

Risposte:


864

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);
    }
}

Documentazione .


160
Un'altra alternativa alla sincronizzazione o threadlocal è quella di rendere la variabile una variabile locale. I var locali sono sempre sicuri per i thread. Presumo che sia una cattiva pratica rendere locali i DateFormat perché sono costosi da creare, ma non ho mai visto metriche solide su questo argomento.
Julien Chastang

18
Questo è un prezzo elevato da pagare per l'hacking SimpleDateFormat. Forse è meglio usare un'alternativa thread-safe . Se sei d'accordo che i singoli sono cattivi, allora ThreadLocalè anche peggio.
Alexander Ryzhov,

4
@overthink C'è qualche motivo per dichiarare ThreadLocal statico e finale, intendo prestazioni o qualcosa del genere?
Sachin Gorade,

4
Il metodo ThreadLocal.get () chiamerà ThreadLocal.initialValue () (una volta) per ogni thread, il che significa che viene creato un oggetto SimpleDateFormat per ogni thread. Non è meglio quindi avere SimpleDateFormat come variabili locali (dal momento che non abbiamo a che fare con problemi di garbage collection)?
sudeepdino008,

3
Fai solo attenzione a non utilizzare l'inizializzazione con doppio controvento per SimpleDateFormat perché ciò creerebbe una classe anonima in modo che il tuo caricatore di classi non possa essere garbage collection. perdita di memoria: restituisce il nuovo SimpleDateFormat () {{applyPattern ("yyyyMMdd HHmm")}};
user1944408,

427

Poiché a ThreadLocalè un riferimento ai dati all'interno di un dato Thread, è possibile finire con perdite di caricamento di classe quando si usano ThreadLocals nei server delle applicazioni usando pool di thread. È necessario essere molto attenti a ripulire eventuali ThreadLocals 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 Threadriferimento di ciascuna alla classe o alle classi della tua webapp poiché Threadnon è 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 spacee dopo che alcuni googling probabilmente aumenteranno -XX:MaxPermSizeinvece 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 ThreadLocalproblemi che stavo riscontrando .


11
Alex Vasseur ha spostato il suo blog. Ecco un collegamento corrente all'articolo di perdita di memoria.
Kenster,

12
Sembra che il thread a cui Julien si collega sia spostato qui e che vale la pena leggere ...
Donal Fellows,

28
Si tratta di moltissimi voti positivi per una risposta che, sebbene informativa, non risponde in alcun modo alla domanda.
Robin,

8
Ora che PermGen viene ucciso da Java 8, questo cambia questa risposta in qualche modo?
Lavatrice malvagia,

13
@Robin: non sono d'accordo. La domanda è su come usare ThreadLocal correttamente, per avere una completa comprensione di qualsiasi concetto (ThreadLocal qui) è anche importante capire come non usarlo e i rischi di usarlo con noncuranza, ed è questa la risposta di Phil. Sono contento che abbia coperto quel punto che non è coperto in altre risposte. Tutti quei voti sono meritati. Credo che SO dovrebbe costruire la comprensione dei concetti piuttosto che essere solo un sito di controllo qualità.
Saurabh Patil,

166

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").


4
Questo è esattamente il modo in cui il framework exPOJO (www.expojo.com) consente l'accesso a ORM Session / PersistenceManager senza il sovraccarico di annotazioni e iniezioni. È un po 'come "iniezione di filo" invece di "iniezione di oggetto". Fornisce l'accesso alle dipendenze senza il requisito (e il sovraccarico) di averle incorporate in ogni oggetto che potrebbe aver bisogno di tali dipendenze. È incredibile come 'leggero' sia possibile realizzare un framework DI quando si utilizza l'iniezione di thread anziché il classico DI (ad es. Spring, ecc.)
Volksman,

27
Perché ho dovuto scorrere fino a quel momento per trovare questa risposta essenziale !?
Jeremy Stein,

1
@Esko, Nel tuo progetto, il tuo uso di ThreadLocal in hashcode ed equals non è necessario. Un approccio migliore è quello di creare o passare un riferimento a una funzione hashcode / equals ogni volta che è necessario. In questo modo, puoi evitare completamente la necessità dell'hack di ThreadLocal.
Pacerier,


1
Questa è la migliore spiegazione dell'uso di TL.
Dexter,

52

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.


13
Quindi dovresti evitare i thread locali allo stesso modo in cui eviti i globali. Non posso assolutamente accettare che sia OK creare globali (thread locali) invece di passare valori in giro, alla gente non piace perché rivela spesso problemi di architettura che non vogliono risolvere.
Juan Mendes,

10
Forse ... Può essere utile, tuttavia, se si dispone di un enorme codebase esistente al quale è necessario aggiungere un nuovo dato che deve essere passato ovunque , ad esempio un contesto di sessione, una transazione db, un utente connesso, ecc. .
Cornel Masson

6
Complimenti per l'utilizzo di datum anziché di dati.
profondo

Questo mi ha incuriosito. 'datum' is the singular form and 'data' is the plural form.
Siddhartha,

23

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.


18

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 può essere utilizzato per memorizzare la traccia dell'esecuzione
gaurav

13
  1. 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.

  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 l'un l'altro la variabile ThreadLocal.

  3. 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.

  4. 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


ThreadLocal può essere utilizzato per memorizzare la traccia dell'esecuzione
gaurav

11

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.


19
Per impostazione predefinita, vengono condivisi oggetti statici o oggetti passati esplicitamente tra thread in cui entrambi i thread hanno un riferimento allo stesso oggetto. Gli oggetti dichiarati localmente in un thread non sono condivisi (sono locali nello stack del thread). Volevo solo chiarirlo.
Ian Varley,

@IanVarley: Grazie per averlo indicato. Quindi se abbiamo una variabile dichiarata e utilizzata nella classe MyThread, allora abbiamo bisogno di ThreadLocal in quel caso? Esempio: la classe MyThread estende Thread {String var1 ;, int var2; } in questo caso var1 e var2 faranno parte del proprio stack di thread e non sono condivisi, quindi in questo caso è necessario ThreadLocal? Potrei sbagliarmi completamente nella mia comprensione, per favore guida.
Jayesh,

4
Se ogni thread vuole avere la propria copia di qualcosa, perché non può semplicemente essere dichiarato locale (che è sempre thread-safe)?
sudeepdino008,

@ sudeepdino008 non hai sempre il controllo della creazione di quei thread (framework webapp, librerie
threadpool

10

Il server Webapp può mantenere un pool di thread e un ThreadLocalvar dovrebbe essere rimosso prima della risposta al client, quindi il thread corrente potrebbe essere riutilizzato dalla richiesta successiva.


8

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()));
    } 

}

Non ho ancora visto il vantaggio di usare ThreadLocal per generare l'id univoco nell'esempio uno se tutto il tuo scopo è di restituire un valore intero univoco incrementale. `` `ThreadId di classe pubblica {// Numero intero atomico contenente l'ID del thread successivo da assegnare AtomicInteger finale statico privato nextId = new AtomicInteger (0); // Restituisce l'ID univoco del thread corrente, assegnandolo se necessario public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen

7

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 ThreadLocalnon 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.Supplierlambda che viene valutata solo quando ThreadLocal#getviene chiamata ma il valore non è stato precedentemente valutato.


5

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.


2
Il rientro non è un problema se il codice è pronto a gestirlo. All'ingresso, annotare se la variabile è già impostata e all'uscita ripristinare il valore precedente (se presente) o rimuoverlo (in caso contrario).
supercat

1
@Jeff, amico, è vero per ogni codice che scrivi, non solo per lo ThreadLocalschema. 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.
Pacerier,

@Jeff, esempio di codice?
Pacerier,

5

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.


5

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 ThreadLocalvariabile 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 ThreadLocals 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.


ThreadLocals NON è uno stato globale a meno che non lo si trasformi in uno stato globale. Sono praticamente sempre risorse dello stack accessibili. Puoi simulare il thread locale semplicemente passando quella variabile in tutti i tuoi metodi (che è ritardato); questo non lo rende uno stato globale ...
Enerccio,

È una forma di stato globale, nel senso che è accessibile da qualsiasi parte del codice (nello stesso contesto del thread). Questo comporta tutte le ripercussioni, come non riuscire a ragionare su chi legge e scrive questo valore. L'uso di un parametro di funzione non è una cosa ritardata, è una buona pratica promuovere interfacce pulite. Tuttavia, sono d'accordo con te sul fatto che passare un parametro su tutta la profondità della tua base di codice sia un odore di codice. Ma credo anche che in molte occasioni l'uso di ThreadLocal sia in primo luogo un odore di codice che ti ha portato qui, quindi questo è ciò che dovresti riconsiderare.
Dimos,

Può semplicemente essere una parte dello stato dell'oggetto, non deve essere utilizzato a livello globale. Certo, si ottiene un po 'di sovraccarico, ma se più oggetti devono avere uno stato diverso per thread, è possibile utilizzare ThreadLocalcome campo oggetto ...
Enerccio

Hai ragione. Probabilmente mi sono imbattuto in diversi abusi di ThreadLocal, dove era accessibile a livello globale. Come hai detto, può ancora essere usato come un campo di classe che limita la visibilità.
Dimos,

4

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 Validatorcon un diverso MessageInterpolator, ma non è possibile specificare il Localequando si chiama validate. Quindi potresti creare un ThreadLocal<Locale>contenitore statico (o meglio ancora, un contenitore generale con altre cose che potresti essere necessario ThreadLocale quindi avere la tua MessageInterpolatorscelta personalizzata Localeda quella. Il passo successivo è scrivere un ServletFilterche utilizza un valore di sessione o request.getLocale()scegliere la locale e l'archivio nel tuo ThreadLocalriferimento.


4

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.


4

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 Threadin Java contiene ThreadLocalMapal 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.threadLocalMapcontiene 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


3

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.


2
In questo esempio è un grosso problema: chi è responsabile della chiusura della connessione? Invece di creare questa connessione come valore iniziale, lascia che l'utente della connessione crei esplicitamente la connessione e la associ al thread tramite ThreadLocal. Il creatore della connessione è quindi anche responsabile della chiusura. Questo non è chiaro in questo esempio. La creazione e l'associazione della connessione possono anche essere nascoste in un semplice framework tramite qualcosa come Transaction.begin () e Transaction.end ().
Rick-Rainer Ludwig,

2

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.

Leggi di più


2

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)


2

[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.


1

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.


1

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();

}
}

1

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.


0

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'.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.