Domanda

Quando si dovrebbe utilizzare un ThreadLocal variabile?

Come si usa?

È stato utile?

Soluzione

Un uso possibile (e comune) è quando si dispone di un oggetto non thread-safe, ma si desidera evitare sincronizzazione accesso a quell'oggetto (ti sto guardando, SimpleDateFormat ). Invece, assegna a ogni thread la propria istanza dell'oggetto.

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

Altri suggerimenti

Poiché un ThreadLocal è un riferimento ai dati all'interno di un determinato Thread, è possibile finire con perdite di caricamento di classe quando si utilizzano get() nei server applicazioni utilizzando pool di thread. Devi stare molto attento a ripulire tutti i set() s remove() o java.lang.OutOfMemoryError: PermGen space usando il metodo -XX:MaxPermSize <=>.

Se non ripulisci quando hai finito, tutti i riferimenti che contiene alle classi caricate come parte di una webapp distribuita rimarranno in heap permanente e non sarà mai raccogliere la spazzatura. La ridistribuzione / non distribuzione della webapp non ripulirà ogni riferimento di <=> alle classi della tua webapp poiché <=> non appartiene alla 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 di <=> e dopo aver cercato su google probabilmente aumenterà solo <=> invece di correggere il bug.

Se si verificano questi problemi, è possibile determinare quale thread e classe conserva questi riferimenti utilizzando Eclipse's Memory Analyzer e / o seguendo Guida di Frank Kieviet e followup .

Aggiornamento: riscoperto Blog di Alex Vasseur che mi ha aiutato a rintracciare alcuni <=> problemi che stavo riscontrando.

Molti framework usano 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 utilizzare ThreadLocals durante l'implementazione di ambiti personalizzati per gli oggetti iniettati (Guice's default < a href = "https://github.com/google/guice/wiki/Scopes" rel = "nofollow noreferrer"> ambiti servlet molto probabilmente li usano anche loro).

ThreadLocals è un tipo 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 qui in " 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 un & Quot; contesto & Quot; variabile.

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.

C'è un ottimo esempio nel libro Concorrenza Java in pratica . Dove l'autore ( Joshua Bloch ) spiega come il confinamento dei thread è uno dei modi più semplici per raggiungere la sicurezza dei thread e ThreadLocal è un 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 di 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ò è conveniente 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 & # 8220; nascosto & # 8221; argomenti del metodo. 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 hai bisogno di un valore della variabile per dipendere dal thread corrente e non è conveniente per te allegare il valore al thread in qualche altro modo (ad esempio, thread di sottoclasse).

Un caso tipico è dove un altro framework ha creato il thread in cui il tuo codice è in esecuzione, ad es. un contenitore servlet, o dove ha più senso usare ThreadLocal perché la tua variabile è quindi " nella sua posizione logica " (anziché una variabile che pende da una sottoclasse di Thread o in un'altra mappa hash).

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

Alcune persone sostengono di usare ThreadLocal come modo per allegare un " thread ID " a ciascun thread in determinati algoritmi simultanei in cui è necessario un numero di thread (vedere ad esempio Herlihy & amp; Shavit). In questi casi, controlla che stai davvero ottenendo un vantaggio!

La documentazione lo dice molto bene : " ogni thread che accede a [una variabile thread-local] (tramite il suo metodo get o set) ha una propria copia inizializzata in modo indipendente della variabile " ;.

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

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

  1. ThreadLocal in Java, è stato introdotto nel JDK 1.2, ma fu in seguito generified in JDK 1.5 per introdurre il tipo di sicurezza sul ThreadLocal variabile.

  2. ThreadLocal può essere associato con un Filo d'ambito, tutto il codice che viene eseguito dal Thread ha accesso a ThreadLocal variabili, ma due thread, non è possibile vedere ogni altri ThreadLocal variabile.

  3. Ogni thread contiene un esclusivo copia di ThreadLocal variabile che diventa ammissibili per la Garbage collection dopo il thread finito, o è morto, normalmente o a qualsiasi Eccezione, Dato quelle ThreadLocal variabile non ha altri riferimenti attivi.

  4. ThreadLocal variabili in Java sono generalmente privati campi statici in Classi e mantenere il suo stato all'interno del Thread.

Leggi di più: ThreadLocal in Java - Esempio di Programma di Esercitazione e di

Due casi d'uso in cui è possibile utilizzare la variabile threadlocal -
1- Quando abbiamo l'obbligo di associare lo stato a un thread (ad es. Un ID utente o ID transazione). Ciò accade in genere con un'applicazione Web che ogni richiesta indirizzata a un servlet ha un ID transazione univoco 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<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();
    }
}

Nota che qui il metodo withInitial è 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 di 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 per java.util.function.Supplier non accetta alcun parametro, è possibile semplicemente utilizzare i riferimenti al metodo (introdotti in Java 8):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

Nota: La valutazione è pigra poiché stai passando ThreadLocal#get lambda che viene valutata solo quando viene chiamato <=> 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 & Quot; re-entrant. & Quot;

Possono accadere cose brutte 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.

quando?

Quando un oggetto non è thread-safe, invece della sincronizzazione che ostacola la scalabilità, assegnare un oggetto a ogni thread e mantenerlo scope thread, che è 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 <=> variabile che trattiene 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 <=>, ma l'ho ideato a scopo illustrativo.

Detto questo, tieni presente che <=> 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.

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 utilizzare Locale.getDefault(). Puoi configurare Validator con un MessageInterpolator diverso, ma non c'è modo di specificare Locale quando chiami validate. Quindi potresti creare un ThreadLocal<Locale> statico (o meglio, un contenitore generale con altre cose che potresti aver bisogno di essere ServletFilter e quindi avere il tuo request.getLocale() personalizzato da cui scegliere <=>. Il passo successivo è scrivere un <=> che utilizza un valore di sessione o <=> per selezionare le impostazioni internazionali e memorizzarle nel <=> 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 a conoscenza di Java EE (non hanno accesso a HttpSession o SJEession SessionContext). 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à l'accesso all'oggetto mutabile da parte del multiplo   i thread nel metodo non sincronizzato sono sincronizzati, significa fare   l'oggetto mutabile deve essere immutabile all'interno del metodo.

Questo   si ottiene dando una nuova istanza di oggetto mutabile per ogni thread   prova ad accedervi. Quindi è una copia locale per ogni thread. Questo è alcuni   hack su come rendere variabile l'istanza in un metodo a cui si accede come a   variabile locale. Come sapete, la variabile locale è disponibile solo   al thread, una differenza è; le variabili locali del metodo no   disponibile per il thread una volta che l'esecuzione del metodo è finita come modificabile   l'oggetto condiviso con threadlocal sarà disponibile tra più   metodi fino a quando non lo ripuliremo.

Per definizione:

  

La classe ThreadLocal in Java ti consente di creare variabili che possono   può essere letto e scritto solo dallo stesso thread. Pertanto, anche se due thread   stanno eseguendo lo stesso codice e il codice ha un riferimento a   Variabile ThreadLocal, quindi i due thread non possono vedersi   Variabili ThreadLocal.

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

Raggiungimento del 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 anziché sull'oggetto mutabile.

Se getter () di threadlocal non ha trovato alcun valore con nella threadlocalmap di wrapper.getDateFormatter(); 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();
    }
}

Ora threadlocal.get() chiamerà currentThread.threadLocalMap e questo controllerà che <=> contenga questa (threadlocal) istanza.
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 creazione di una 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 si chiama getConnection, verrà restituita una connessione associata a quel thread. Lo stesso può essere fatto con altre proprietà come dateformat, contesto di transazione che non si desidera condividere tra thread.

Avresti potuto usare anche 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 ti 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ù

[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 molto 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 può vederlo. Vedi 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 sono associati costi di sicurezza dei thread, mentre questo approccio non ne ha.

Esistono 3 scenari per l'utilizzo di un helper di classe come SimpleDateFormat nel codice multithread, il migliore è utilizzare ThreadLocal

Scenari

1- Uso come condividi oggetto con l'aiuto del meccanismo blocco o sincronizzazione che rende l'app lenta

2- Utilizzo come oggetto locale all'interno di un metodo

In questo scenario, se abbiamo 4 thread ognuno chiama un metodo 1000 allora abbiamo
4000 SimpleDateFormat oggetto creato e in attesa che GC li cancelli

3- Uso di ThreadLocal

se abbiamo 4 thread e abbiamo assegnato a ogni thread un'istanza SimpleDateFormat
 quindi abbiamo 4 thread , 4 oggetti di SimpleDateFormat.

Non è necessario il meccanismo di blocco e la creazione e distruzione di oggetti. (Buona complessità temporale e complessità spaziale)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top