"Cześć, Amigo!"

"Cześć, Ellie!"

„Chcę ci opowiedzieć o lotnym modyfikatorze. Wiesz, co to jest?”

— Coś związanego z nitkami. Nie pamiętam dokładnie.

„Więc słuchaj. Oto kilka szczegółów technicznych dla Ciebie:”

„Komputer ma dwa rodzaje pamięci: pamięć globalną (zwykłą) i pamięć wbudowaną w procesor. Wbudowana pamięć procesora jest podzielona na rejestry, pamięć podręczną pierwszego poziomu (L1), pamięć podręczną drugiego poziomu (L2) i trzeci poziom (L3).”

„Te typy pamięci mają różne prędkości. Najszybszą i najmniejszą pamięcią są rejestry, następnie pamięć podręczna procesora (L1, L2, L3), a na koniec pamięć globalna (najwolniejsza).”

„Pamięć globalna i pamięć podręczna procesora działają z bardzo różnymi prędkościami, więc maszyna Java umożliwia każdemu wątkowi przechowywanie najczęściej używanych zmiennych w lokalnej pamięci wątków (w pamięci podręcznej procesora).”

„Czy ten proces można jakoś kontrolować?”

„Niezupełnie. Całą pracę wykonuje maszyna Java. Jest bardzo inteligentna, jeśli chodzi o optymalizację wydajności”.

„Ale właśnie dlatego ci to mówię. Jest jeden mały problem. Kiedy dwa wątki pracują z tą samą zmienną, każdy z nich może przechowywać kopię we własnej lokalnej pamięci podręcznej. Wtedy jeden wątek może zmienić zmienną, ale drugi może nie zauważyć zmiany, ponieważ nadal działa z własną kopią zmiennej”.

— Cóż, w takim razie można zrobić?

„Twórcy Javy przewidzieli specjalne słowo kluczowe dla tej sytuacji: volatile. Jeśli zmienna jest dostępna z różnych wątków, należy ją oznaczyć modyfikatorem volatile, aby maszyna Java nie umieszczała jej w pamięci podręcznej. Tak zwykle wygląda:

public volatile int count = 0;

- Och, pamiętam. Już o tym wspominałeś. Ja już to wiem.

- Pewnie, że tak. Ale przypomniałeś sobie o tym dopiero wtedy, gdy ci powiedziałem.

– Eee, trochę zapomniałem.

„Powtórzenie jest matką nauki!”

„Oto kilka nowych faktów na temat modyfikatora volatile. Modyfikator volatile gwarantuje jedynie, że zmienna będzie bezpiecznie odczytywana i zapisywana. Nie gwarantuje jednak, że zostanie ona bezpiecznie zmieniona”.

"Co za różnica?"

„Spójrz, jak zmienia się zmienna:”

Kod Co naprawdę się dzieje: Opis
count++
register = count;

register = register+1;

count = register;
Krok 1.
Wartość licznika zmiennej jest kopiowana z pamięci globalnej do rejestru procesora.

Krok 2.
Wewnątrz procesora zmienna rejestru jest zwiększana o 1.

Krok 3.
Wartość zmiennej jest kopiowana z procesora do pamięci globalnej.

„Wow! Więc wszystkie zmienne są zmieniane tylko w procesorze?”

"Tak."

„A wartości są kopiowane tam iz powrotem: z pamięci do procesora iz powrotem?”

"Tak."

„Modyfikator volatile gwarantuje, że po uzyskaniu dostępu do zmiennej count zostanie ona odczytana z pamięci (krok 1). A jeśli wątek chce przypisać nową wartość, na pewno znajdzie się ona w pamięci globalnej (krok 3).”

„Ale maszyna Java nie gwarantuje, że nie będzie żadnego przełączania wątków między krokami 1 i 3”.

„Więc zwiększenie zmiennej o 1 to tak naprawdę trzy operacje?”

"Tak."

„A jeśli dwa wątki jednocześnie chcą wykonać count ++, to mogą sobie przeszkadzać?”

"Tak, sprawdź to:"

Wątek 1 Wątek 2 Wynik
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

„Więc możesz uzyskać dostęp do zmiennej, ale jej zmiana jest nadal ryzykowna?”

„Cóż, możesz to zmienić, tylko bądź ostrożny ☺”

"Jak?"

Zsynchronizowany  jest naszym najlepszym przyjacielem”.

"Widzę."