ThreadLocal jest klasą pozwalającą na łatwe przechowywanie wartości dla każdego wątku oddzielnie – każdy wątek ma swoją wartość.

Korzystanie z klasy ThreadLocal
Klasa ThreadLocal udostępnia podstawowe operacje takie jak:
–get()
–set(T value)
–remove()
Dzięki którym możemy przykładowo operować na wartości przypisanej do wątku:
@Test
public void testBasics(){
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("Dog");
assertEquals("Dog", threadLocal.get());
threadLocal.remove();
assertNull(threadLocal.get());
}Jednak w sytuacji gdy mamy tylko jeden wątek nie ma najmniejszego sensu używanie klasy ThreadLocal. Lepiej po prostu użyć zmiennych:
@Test
public void testBasics_simplified(){
String value;
value = "Dog";
assertEquals("Dog", value);
value = null;
assertNull(value);
}Żeby użycie ThreadLocal miało sens, potrzeba jest przynajmniej dwóch wątków:
@Test
public void testTwoThreads() throws InterruptedException {
final ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("Dog");
Thread thread = new Thread(() -> threadLocal.set("Cat"));
thread.start();
thread.join();
assertEquals("Dog", threadLocal.get());
}Jako, że nadpisywanie (patrz threadLocal.set(„Cat”)) wartości odbywa się w oddzielnym wątku, nie wpływa to na wartość ustawioną w wątku wywołującym (patrz threadLocal.set(„Dog”)).
Krok głębiej
Warto zajrzeć do implementacji klasy ThreadLocal, aby lepiej zrozumieć jej działanie (wycinki poniżej zawierają uproszczone wersje oryginalnej klasy).
get()
public T get() {
return get(Thread.currentThread());
}Jak widać implementacja metody get jest prosta i opiera się o wywołanie metody get przekazując do niej aktualny wątek (Thread.currentThread()):
private T get(Thread t) {
ThreadLocalMap map = getMap(t);
if (map != null) {
//pobieranie wartosci z mapy
}
return setInitialValue(t);
}Jak widać wartości dla danego wątku są przechowywane w specjalnej mapie – ThreadLocalMap. I jeśli taka mapa istnieje to następuje pobranie wartości z tej mapy. W innym wypadku ustawiana jest wartość początkowa dla danego wątku.
set(T value)
public void set(T value) {
set(Thread.currentThread(), value);
//...
}Podobnie jak metoda get, metoda set korzysta z aktualnego wątku (Thread.currentThread()). I ustawia w nim wartość za pomocą metody set przyjmującej oprócz wartości, wątek którego ma dotyczyć wartość:
private void set(Thread t, T value) {
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}Jak widać implementacja metody set jest bardzo prosta – jeśli wartość już istnieje, to ją nadpisujemy. Jeśli nie to tworzymy nową z zadaną wartością. I znowu podobnie jak w metodzie get, do obsługi wartości wykorzystywana jest mapa – ThreadLocalMap.
remove()
public void remove() {
remove(Thread.currentThread());
}Metoda remove działa identycznie do poprzednich – pobiera aktualny wątek i przekazuje go do metody przyjmującej jako argument wątek:
private void remove(Thread t) {
ThreadLocalMap m = getMap(t);
if (m != null) {
m.remove(this);
}
}I znowu, logika jest tutaj bardzo prosta – jeśli już jest w mapie wartość dla tego wątku to ją usuń, jeśli nie to po prostu nic nie rób (wszystko cały czas oparte o mapę – ThreadLocalMap).
