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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow