Perché le variabili Java ThreadLocal dovrebbero essere statiche


101

Stavo leggendo JavaDoc per Threadlocal qui

https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html

e dice "Le istanze ThreadLocal sono in genere campi statici privati ​​in classi che desiderano associare lo stato a un thread (ad esempio, un ID utente o un ID transazione)."

Ma la mia domanda è: perché hanno scelto di renderlo statico (tipicamente) - rende le cose un po 'confuse avere lo stato "per thread" ma i campi sono statici?

Risposte:


131

Perché se fosse un campo a livello di istanza, in realtà sarebbe "Per thread - Per istanza", non solo un "Per thread" garantito. Normalmente non è la semantica che stai cercando.

Di solito contiene qualcosa come oggetti che hanno come ambito una conversazione utente, una richiesta Web, ecc. Non vuoi che siano anche sotto-ambito all'istanza della classe.
Una richiesta web => una sessione di persistenza.
Non una richiesta web => una sessione di persistenza per oggetto.


2
Mi piace questa spiegazione perché mostra come deve essere utilizzato
ThreadLocal

4
Per thread per istanza può essere una semantica utile, ma la maggior parte degli usi per quel pattern coinvolgerebbe così tanti oggetti che sarebbe meglio usare a ThreadLocalper contenere un riferimento a un hash-set che mappa gli oggetti su istanze per thread.
supercat

@optional Significa semplicemente che ogni istanza del non statico ThreadLocalconterrebbe i propri dati locali del thread anche se tali ThreadLocalistanze esistono nello stesso thread. Non è necessariamente sbagliato farlo - suppongo che potrebbe essere il modello meno popolare dei due
geg

17

Rendilo statico o se stai cercando di evitare qualsiasi campo statico nella tua classe, rendi la classe stessa un singleton e quindi puoi tranquillamente usare il ThreadLocal a livello di istanza finché hai quel singleton disponibile a livello globale.



3

Il motivo è che si accede alle variabili tramite un puntatore associato al thread. Agiscono come variabili globali con ambito di thread, quindi static è la soluzione più vicina. Questo è il modo in cui ottieni lo stato locale dei thread in cose come pthreads, quindi potrebbe essere solo un incidente di storia e implementazione.


1

Un uso per un threadlocal su un'istanza per thread è se si desidera che qualcosa sia visibile in tutti i metodi di un oggetto e che sia thread-safe senza sincronizzare l'accesso ad esso come si farebbe per un campo ordinario.


1

Fare riferimento a questo , questo fornisce una migliore comprensione.

In breve, l' ThreadLocaloggetto funziona come una mappa valore-chiave. Quando il thread invocaThreadLocal get/set metodo, recupererà / memorizzerà l'oggetto thread nella chiave della mappa e il valore nel valore della mappa. Ecco perché thread diversi hanno una copia di valore diversa (che si desidera memorizzare localmente), perché risiede in una voce di mappa diversa.

Ecco perché hai solo bisogno di una mappa per mantenere tutti i valori. Sebbene non sia necessario, puoi avere più mappe (senza dichiarare statico) per mantenere anche ogni oggetto thread, il che, è totalmente ridondante, ecco perché è preferibile la variabile statica.


-1

static final ThreadLocal le variabili sono thread-safe.

staticrende la variabile ThreadLocal disponibile su più classi solo per il rispettivo thread. è una sorta di decarazione delle variabili globali delle rispettive variabili locali del thread su più classi.

Possiamo controllare la sicurezza del thread con il seguente codice di esempio.

  • CurrentUser - memorizza l'ID utente corrente in ThreadLocal
  • TestService- Servizio semplice con metodo - getUser()per recuperare l'utente corrente da CurrentUser.
  • TestThread - questa classe utilizzata per creare più thread e impostare gli userid contemporaneamente

.

public class CurrentUser

public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();

public static ThreadLocal<String> getCurrent() {
    return CURRENT;
}

public static void setCurrent(String user) {
    CURRENT.set(user);
}

}

public class TestService {

public String getUser() {
    return CurrentUser.getCurrent().get();
}

}

.

import java.util.ArrayList;
import java.util.List;

public class TestThread {

public static void main(String[] args) {

  List<Integer> integerList = new ArrayList<>();

  //creates a List of 100 integers
  for (int i = 0; i < 100; i++) {

    integerList.add(i);
  }

  //parallel stream to test concurrent thread execution
  integerList.parallelStream().forEach(intValue -> {

    //All concurrent thread will set the user as "intValue"
    CurrentUser.setCurrent("" + intValue);
    //Thread creates a sample instance for TestService class
    TestService testService = new TestService();
    //Print the respective thread name along with "intValue" value and current user. 
    System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());

    try {
      //all concurrent thread will wait for 3 seconds
      Thread.sleep(3000l);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    //Print the respective thread name along with "intValue" value and current user.
    System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
  });

}

}

.

Eseguire la classe principale TestThread. Produzione -

Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...

Riepilogo dell'analisi

  1. il thread "principale" si avvia e imposta l'utente corrente come "62", parallelamente il thread "ForkJoinPool.commonPool-worker-2" si avvia e imposta l'utente corrente come "31", parallelamente il thread "ForkJoinPool.commonPool-worker-3" si avvia e imposta corrente utente come "81", parallelamente il thread "ForkJoinPool.commonPool-worker-1" inizia e imposta l'utente corrente come "87" Start-main-> 62-> 62 Start-ForkJoinPool.commonPool-worker-2-> 31-> 31 Start-ForkJoinPool.commonPool-worker-3-> 81-> 81 Start-ForkJoinPool.commonPool-worker-1-> 87-> 87
  2. Tutti questi thread di cui sopra dormiranno per 3 secondi
  3. mainl'esecuzione termina e stampa l'utente corrente come "62", parallelamente l' ForkJoinPool.commonPool-worker-1esecuzione termina e stampa l'utente corrente come "87", parallelamente l' ForkJoinPool.commonPool-worker-2esecuzione termina e stampa l'utente corrente come "31", parallelamente l' ForkJoinPool.commonPool-worker-3esecuzione termina e stampa l'utente corrente come "81"

Inferenza

I thread simultanei sono in grado di recuperare gli userid corretti anche se è stato dichiarato come "ThreadLocal finale statico"

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.