Mateusz Mazurek – programista z pasją

Python, architektura, ciekawostki ze świata IT

Algorytmika Inżynieria oprogramowania Programowanie Programowanie webowe

Wzorzec ThreadLocal w praktyce

Cześć! Cieszę się, że mnie odwiedziłeś/aś. Zanim przejdziesz do artykułu chciałbym zwrocić Ci uwagę na to, że ten artykuł był pisany kilka lat temu (2014-11-29) miej więc proszę na uwadzę że rozwiązania i przemyślenia które tu znajdziesz nie muszą być aktualne. Niemniej jednak zachęcam do przeczytania.

Cześć,
dawno niczego nie pisałem tutaj, niestety czas nie pozwala mi prowadzić tego bloga tak systematycznie jakbym chciał. Dziś jednak znalazło się trochę czasu między pisaniem pracy inż, pracą, dziewczyną i życiem – co zaowocowało takim oto, krótkim wpisem.

Zajmiemy się dziś wzorcem ThreadLocal, czyli rozwiązaniem problemu, kiedy to chcemy aby jakaś zmienna udostępniała swoją kopię każdemu wątkowi który o nią zapyta.

Spójrzmy na taki scenariusz:

Mamy klasę o nazwie VariableContainer. Zawiera ona jedno pole typu int o nazwie variable. Jest to pole statyczne, gdyż chcemy aby odnosił się on do typu a nie konkretnego obiektu. I takiej konstrukcji używamy w naszym projekcie.

Teraz zdarza się jakiś case, gdzie musimy podzielić naszą aplikację na wątki. A więc n wątków będzie pracować na naszej zmiennej. Niestety podczas pisania tego kodu, okazuje się że każdy wątek powinien mieć w swojej pamięci lokalnej kopię naszej zmiennej variable i dopiero na niej pracować… Jak to rozwiązać? Zerknijmy pierw na kod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class VariableContainer
{
    public static void main(String args[]) {
        for(int i=0;i<20;i++)
        {
            NamedThread nt = new NamedThread(i);
            nt.start();
        }
       
        System.out.println("Main: "+VariableContainer.get());
    }

    public VariableContainer() {
    }
    private static int variable = 0;
   
    public static void increaseBy(int howMuch){
        variable+=howMuch;
    }
    public static int get(){
        return variable;
    }
}

gdzie NamedThread to klasa dziedzicząc po wątku, symuluje ona pracę na zmiennej variable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NamedThread extends Thread {

    private int number;
   
    public NamedThread(int number) {

        this.number=number;
       
    }
    @Override
    public void run() {
        VariableContainer.increaseBy(4);
        System.out.println("Value for thread nr."+number+" is:"+VariableContainer.get());
    }
   
}

Kod taki jak wyżej oczywiście daje na wyjściu bzdury:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Value for thread nr.1 is:4
Value for thread nr.2 is:12
Value for thread nr.0 is:8
Value for thread nr.5 is:16
Value for thread nr.3 is:24
Value for thread nr.6 is:24
Main: 36
Value for thread nr.17 is:40
Value for thread nr.13 is:36
Value for thread nr.10 is:44
Value for thread nr.9 is:32
Value for thread nr.14 is:48
Value for thread nr.18 is:52
Value for thread nr.4 is:52
Value for thread nr.7 is:56
Value for thread nr.8 is:60
Value for thread nr.11 is:64
Value for thread nr.12 is:68
Value for thread nr.15 is:72
Value for thread nr.16 is:76
Value for thread nr.19 is:80

To co widzimy jest wielce dalekie od zamierzonego efektu. Chcielibyśmy aby wartość zmiennej variable nie była dzielona pomiędzy wątki.
Możemy do tego użyć wzorca ThreadLocal. Istnieje klasa opakowująca o tej samej nazwie w Javie. Możemy użyć jej w taki sposób:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class VariableContainer

{
    public static void main(String args[]) {
       
        for(int i=0;i<20;i++)
        {
            NamedThread nt = new NamedThread(i);
            nt.start();
        }
       
        System.out.println("Main: "+VariableContainer.get());
    }

    private static ThreadLocal<Integer> variable = new ThreadLocal<Integer>(){
        @Override protected Integer initialValue(){
            return 0;
        }
    };
   
    public static void increaseBy(int howMuch){
        variable.set( variable.get()+howMuch );
    }
    public static int get(){
        return variable.get();
    }
}

Dzieki takiej zmianie kodu, wyjście prezentuje się znaczenie poprawniej:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Value for thread nr.1 is:4
Value for thread nr.5 is:4
Value for thread nr.9 is:4
Value for thread nr.2 is:4
Value for thread nr.13 is:4
Value for thread nr.6 is:4
Value for thread nr.17 is:4
Main: 0
Value for thread nr.10 is:4
Value for thread nr.0 is:4
Value for thread nr.14 is:4
Value for thread nr.18 is:4
Value for thread nr.3 is:4
Value for thread nr.4 is:4
Value for thread nr.7 is:4
Value for thread nr.8 is:4
Value for thread nr.11 is:4
Value for thread nr.12 is:4
Value for thread nr.15 is:4
Value for thread nr.16 is:4
Value for thread nr.19 is:4

Jak widać, teraz zmienna wartość zmiennej variable jest dzielona między wątki, zachowujemy wartość odpowiednią dla wątku głównego oraz dajemy, każdemu innemu, swoją własną kopię wartości tej zmiennej.

Warto znać takie zastosowania, czasem się przydają ;)

Pozdrawiam,
Mateusz M.

Dzięki za wizytę,
Mateusz Mazurek

A może wolisz nowości na mail?

Subskrybuj
Powiadom o
guest

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

0 komentarzy
Inline Feedbacks
View all comments