Java Language
ThreadLocal
Szukaj…
Uwagi
Najlepiej nadaje się do obiektów, które zależą od elementów wewnętrznych podczas wywoływania połączenia, ale w innym przypadku są bezstanowe, takie jak SimpleDateFormat
, Marshaller
W przypadku Random
użycia ThreadLocal należy rozważyć użycie ThreadLocalRandom
Inicjalizacja funkcjonalna ThreadLocal Java 8
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);
}
}
Podstawowe użycie ThreadLocal
Java ThreadLocal
służy do tworzenia lokalnych zmiennych wątków. Wiadomo, że wątki obiektu dzielą swoje zmienne, więc zmienna nie jest bezpieczna dla wątków. Możemy użyć synchronizacji dla bezpieczeństwa wątków, ale jeśli chcemy uniknąć synchronizacji, ThreadLocal pozwala nam tworzyć zmienne, które są lokalne dla wątku, tj. Tylko ten wątek może odczytywać lub zapisywać te zmienne, więc inne wątki wykonują ten sam fragment kodu nie będą mogli uzyskiwać dostępu do zmiennych ThreadLocal.
Można to wykorzystać możemy użyć zmiennych ThreadLocal
. w sytuacjach, gdy masz pulę wątków, na przykład w serwisie internetowym. Na przykład tworzenie obiektu SimpleDateFormat
każdym razem dla każdego żądania jest czasochłonne, a obiektu statycznego nie można utworzyć, ponieważ SimpleDateFormat
nie jest bezpieczny dla wątków, dlatego możemy utworzyć ThreadLocal, abyśmy mogli wykonywać operacje bezpieczne dla wątków bez konieczności tworzenia SimpleDateFormat
każdego czas.
Poniższy fragment kodu pokazuje, jak można go użyć:
Każdy wątek ma własną zmienną ThreadLocal
i może używać metod get()
i set()
celu uzyskania wartości domyślnej lub zmiany jej wartości lokalnej na Thread.
Instancje ThreadLocal
są zwykle prywatnymi polami statycznymi w klasach, które chcą powiązać stan z wątkiem.
Oto mały przykład pokazujący użycie ThreadLocal w programie Java i udowadniający, że każdy wątek ma własną kopię zmiennej 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());
}
}
Wynik:
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
Jak widać z danych wyjściowych, że Thread-0 zmienił wartość formatera, ale nadal domyślny formatter wątku 2 jest taki sam jak wartość zainicjowana.
Wiele wątków z jednym wspólnym obiektem
W tym przykładzie mamy tylko jeden obiekt, ale jest on dzielony pomiędzy / wykonywany w różnych wątkach. Zwykłe użycie pól do zapisania stanu nie byłoby możliwe, ponieważ drugi wątek też to zobaczy (lub prawdopodobnie nie zobaczy).
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();
}
}
W Foo liczymy zaczynając od zera. Zamiast zapisywać stan w polu, przechowujemy nasz bieżący numer w obiekcie ThreadLocal, który jest statycznie dostępny. Zauważ, że synchronizacja w tym przykładzie nie jest związana z użyciem ThreadLocal, ale raczej zapewnia lepszą wydajność konsoli.
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) {
}
}
}
}
}
Z danych wyjściowych możemy zobaczyć, że każdy wątek liczy się dla siebie i nie używa wartości drugiego:
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