Java Language
ThreadLocal
Ricerca…
Osservazioni
Ideale per oggetti che dipendono da internals durante il richiamo di una chiamata, ma altrimenti sono stateless, come SimpleDateFormat
, Marshaller
Per l'utilizzo di ThreadLocal Random
, prendere in considerazione l'utilizzo di ThreadLocalRandom
Inizializzazione funzionale di Java 8 ThreadLocal
public static class ThreadLocalExample
{
private static final ThreadLocal<SimpleDateFormat> format =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd_HHmm"));
public String formatDate(Date date)
{
return format.get().format(date);
}
}
Utilizzo thread di base
Java ThreadLocal
è usato per creare variabili locali del thread. È noto che i thread di un oggetto condividono le sue variabili, quindi la variabile non è thread-safe. Possiamo usare la sincronizzazione per la sicurezza dei thread, ma se vogliamo evitare la sincronizzazione, ThreadLocal ci permette di creare variabili che sono locali al thread, cioè solo che quel thread può leggere o scrivere su quelle variabili, quindi gli altri thread eseguono lo stesso pezzo di codice non sarà in grado di accedere alle altre variabili ThreadLocal.
Questo può essere usato possiamo usare variabili ThreadLocal
. in situazioni in cui si dispone di un pool di thread, ad esempio in un servizio Web. Ad esempio, la creazione di un oggetto SimpleDateFormat
ogni volta per ogni richiesta richiede molto tempo e non è possibile creare uno statico in quanto SimpleDateFormat
non è thread-safe, quindi possiamo creare un ThreadLocal in modo da poter eseguire operazioni thread-safe senza l'overhead di creare SimpleDateFormat
ogni tempo.
Il seguente codice mostra come può essere usato:
Ogni thread ha la propria variabile ThreadLocal
e possono usare i suoi metodi get()
e set()
per ottenere il valore predefinito o cambiarne il valore local in Thread.
ThreadLocal
istanze ThreadLocal
sono in genere campi statici privati in classi che desiderano associare lo stato a un thread.
Ecco un piccolo esempio che mostra l'uso di ThreadLocal nel programma java e dimostra che ogni thread ha la propria copia della variabile ThreadLocal
.
package com.examples.threads;
import java.text.SimpleDateFormat;
import java.util.Random;
public class ThreadLocalExample implements Runnable{
// SimpleDateFormat is not thread-safe, so give one to each thread
// 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 static void main(String[] args) throws InterruptedException {
ThreadLocalExample obj = new ThreadLocalExample();
for(int i=0 ; i<10; i++){
Thread t = new Thread(obj, ""+i);
Thread.sleep(new Random().nextInt(1000));
t.start();
}
}
@Override
public void run() {
System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
formatter.set(new SimpleDateFormat());
System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
}
}
Produzione:
Thread Name= 0 default Formatter = yyyyMMdd HHmm
Thread Name= 1 default Formatter = yyyyMMdd HHmm
Thread Name= 0 formatter = M/d/yy h:mm a
Thread Name= 2 default Formatter = yyyyMMdd HHmm
Thread Name= 1 formatter = M/d/yy h:mm a
Thread Name= 3 default Formatter = yyyyMMdd HHmm
Thread Name= 4 default Formatter = yyyyMMdd HHmm
Thread Name= 4 formatter = M/d/yy h:mm a
Thread Name= 5 default Formatter = yyyyMMdd HHmm
Thread Name= 2 formatter = M/d/yy h:mm a
Thread Name= 3 formatter = M/d/yy h:mm a
Thread Name= 6 default Formatter = yyyyMMdd HHmm
Thread Name= 5 formatter = M/d/yy h:mm a
Thread Name= 6 formatter = M/d/yy h:mm a
Thread Name= 7 default Formatter = yyyyMMdd HHmm
Thread Name= 8 default Formatter = yyyyMMdd HHmm
Thread Name= 8 formatter = M/d/yy h:mm a
Thread Name= 7 formatter = M/d/yy h:mm a
Thread Name= 9 default Formatter = yyyyMMdd HHmm
Thread Name= 9 formatter = M/d/yy h:mm a
Come possiamo vedere dall'output, Thread-0 ha cambiato il valore di formattatore, ma il formattatore di default di thread-2 è lo stesso del valore inizializzato.
Più thread con un oggetto condiviso
In questo esempio abbiamo un solo oggetto ma è condiviso tra / eseguito su thread differenti. L'uso normale dei campi per salvare lo stato non sarebbe possibile perché anche l'altro thread lo vedrebbe (o probabilmente non vedrebbe).
public class Test {
public static void main(String[] args) {
Foo foo = new Foo();
new Thread(foo, "Thread 1").start();
new Thread(foo, "Thread 2").start();
}
}
In Foo contiamo partendo da zero. Invece di salvare lo stato in un campo, memorizziamo il nostro numero corrente nell'oggetto ThreadLocal che è staticamente accessibile. Si noti che la sincronizzazione in questo esempio non è correlata all'utilizzo di ThreadLocal ma assicura un output della console migliore.
public class Foo implements Runnable {
private static final int ITERATIONS = 10;
private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public void run() {
for (int i = 0; i < ITERATIONS; i++) {
synchronized (threadLocal) {
//Although accessing a static field, we get our own (previously saved) value.
int value = threadLocal.get();
System.out.println(Thread.currentThread().getName() + ": " + value);
//Update our own variable
threadLocal.set(value + 1);
try {
threadLocal.notifyAll();
if (i < ITERATIONS - 1) {
threadLocal.wait();
}
} catch (InterruptedException ex) {
}
}
}
}
}
Dall'output possiamo vedere che ogni thread conta per se stesso e non usa il valore dell'altro:
Thread 1: 0
Thread 2: 0
Thread 1: 1
Thread 2: 1
Thread 1: 2
Thread 2: 2
Thread 1: 3
Thread 2: 3
Thread 1: 4
Thread 2: 4
Thread 1: 5
Thread 2: 5
Thread 1: 6
Thread 2: 6
Thread 1: 7
Thread 2: 7
Thread 1: 8
Thread 2: 8
Thread 1: 9
Thread 2: 9