Zmienne klasy kontra zmienne lokalne a wpływ na wielowątkowość ⚔️

Jedną z najważniejszych rzeczy podczas tworzenia aplikacji webowych, jest upewnienie się, że będą one działać bezpiecznie przy dostępie wielowątkowym. Jednym z istotnych elementów, które należy wziąć pod uwagę, jest widoczność zmiennych.
W tym wpisie tłumaczę, czym różnią się zmienne klasy od zmiennych metod w kontekście wielowątkowości.

Weźmy pod uwagę poniższą klasę Employee. Mamy w niej do czynienia ze zmienną klasową workedHours oraz ze zmienną lokalną hoursSum w metodzie yearlyHours.

W kontekście wielowątkowości obie te zmienne mają odmienną charakterystykę.

class Employee {

    private Map<Long, Long> workedHours;

    public long yearlyHours() {
        long hoursSum = 0;
        for(long hours: workedHours.values()) {
            hoursSum += hours;
        }
        return hoursSum;
    }

    public void logHours(long month, long hours) {
        workedHours.set(month, workedHours.get(month) + hours);
    }

}

Zmienna workedHours podlega dostępowi wielowątkowemu. W momencie, w którym wiele wątków na raz korzysta z tej samej instancji klasy Employee, może łatwo dojść do warunków wyścigu (ang. race condition), w wyniku którego dane zapisane w obiekcie wskazywanym przez tą zmienną będą niepoprawne.

Z kolei zmienna hoursSum nigdy nie będzie widoczna dla wielu wątków jednocześnie. Każdy wątek wywołując metodę yearlyHours tworzy własną instancję i nie współdzieli jej z innymi wątkami.

Co to dla mnie oznacza?

Konsekwencje są dwie.

Po pierwsze, za każdym razem, kiedy tworzysz nowe zmienne w ciałach metod i nie udostępniasz ich nigdzie indziej, możesz nie przejmować się dostępem wielowątkowym. Takie zmienne są widoczne tylko i wyłącznie dla wątku, który je utworzył.

Z kolei, gdy mówimy o zmiennych klasowych, wówczas musisz się upewnić, że podczas wielowątkowego dostępu nie będą one modyfikowane jednocześnie przez kilka wątków. Tym samym prowadząc do niepoprawnych danych.

Co należy więc zrobić z powyższym fragmentem kodu?

Należy się upewnić, że mapa, na którą wskazuje workedHours może być bezpiecznie używana przez wiele wątków jednocześnie.

Najprostsze rozwiązanie to otoczenie metody logHours klauzulą synchronized.

class Employee {

    private Map<Long, Long> workedHours;

    public synchronized void logHours(long month, long hours) {
        workedHours.set(month, workedHours.get(month) + hours);
    }

}

Należy wtedy pamiętać, by inne użycia tej zmiennej również opatrzyć takim słowem kluczowym. Oraz by nie udostępniać tej zmiennej na zewnątrz klasy!

Drugie rozwiązanie polega natomiast w tym wypadku na zastosowaniu implementacji mapy dedykowanej dostępowi współbieżnemu, czyli ConcurrentHashMap.

Wtedy metoda logHours mogłaby wyglądać tak:

class Employee {

    private Map<Long, Long> workedHours = new ConcurrentHashMap<>();

    public void logHours(long month, long hours) {
        workedHours.compute(month, (key, current) -> current + hours);
    }

}

Pozbywamy się słowa kluczowego synchronized a wewnętrzne mechanizmy klasy ConcurrentHashMap zapobiegają współbieżnemu dostępowi do wpisu pod kluczem month.

Co powinienem z tego zapamiętać?

  1. Zmienne klasy i zmienne w metodach mają odmienną charakterystykę pod kątem dostępu wielowątkowego.
  2. Zmienne klasy mogą być odczytywane i modyfikowane przez wiele wątków naraz, dlatego należy zadbać o ich odpowiednie zabezpieczenie
  3. Zmienne tworzone w metodach są widoczne tylko dla wątku, które je stworzył. Dlatego nie trzeba martwić się synchronizacją przy dostępie do nich.
  4. Referencje do zmiennych klasy znajdują się na stercie (heap), a referencje do zmiennych metod znajdują się na stosie (stack) aktualnie wykonującego się wątku.

Author: Dariusz Mydlarz

Cześć, nazywam się Dariusz Mydlarz i od 2012 pracuję jako programista. Tworzę w ekosystemie Javy i uwielbiam systemy backendowe. Chcę w tym miejscu pomagać Ci stawać się lepszym programista. Jeśli mogę Ci jakoś pomoc, po prostu napisz do mnie.

2 Replies to “Zmienne klasy kontra zmienne lokalne a wpływ na wielowątkowość ⚔️

  1. Drobna uwaga: ‚zmienne klasy’ to pola statyczne w klasie i ich ‚właścicielem’ jest rzeczywiście klasa. U Ciebie ‚zmienne klasy’ to zwykłe pola, ale ich właścicielem nie jest klasa tylko obiekt, więc nazwa ‚zmienne klasy’ jest nieadekwatna.

    1. Tak, chodzi o zmienne / pola niestatyczne, które należą do konkretnej instancji klasy (obiektu). Dzięki za doprecyzowanie 👍

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *