"Bună, Amigo!"

— Bună, Ellie!

"Vreau să vă spun despre modificatorul volatil. Știți ce este?"

— Ceva de-a face cu firele. Nu-mi amintesc exact.

"Atunci, ascultă. Iată câteva detalii tehnice pentru tine:"

„Un computer are două tipuri de memorie: memorie globală (obișnuită) și memorie încorporată în procesor. Memoria încorporată a procesorului este împărțită în registre, un cache de prim nivel (L1), cache de al doilea nivel (L2) și al treilea nivel (L3)."

„Aceste tipuri de memorie au viteze diferite. Memoria cea mai rapidă și cea mai mică sunt registrele, apoi memoria cache a procesorului (L1, L2, L3) și în sfârșit memoria globală (cea mai lentă).”

„Memoria globală și memoria cache a procesorului funcționează la viteze extrem de diferite, astfel încât mașina Java permite fiecărui thread să stocheze variabilele cele mai frecvent utilizate în memoria thread-ului local (în memoria cache a procesorului).”

„Poate fi controlat cumva acest proces?”

"Nu chiar. Toată munca este realizată de mașina Java. Este foarte inteligent când vine vorba de optimizarea performanței."

„Dar iată de ce vă spun asta. Există o mică problemă. Când două fire lucrează cu aceeași variabilă, fiecare poate stoca o copie în propriul său cache local. Și apoi un thread poate schimba variabila, dar al doilea s-ar putea să nu vadă schimbarea, deoarece încă funcționează cu propria copie a variabilei.”

— Ei bine, ce se poate face atunci?

„Creatorii Java au furnizat un cuvânt cheie special pentru această situație: volatile. Dacă o variabilă este accesată din fire diferite, trebuie să o marcați cu modificatorul volatil, astfel încât mașina Java să nu o pună în cache. Așa este de obicei arata:"

public volatile int count = 0;

"Oh, îmi amintesc. Ai menționat deja asta. Știu deja asta."

— Sigur că da. Dar ţi-ai amintit doar când ţi-am spus.

— Ei bine, am uitat puțin.

„Repetarea este mama învăţăturii!”

"Iată câteva fapte noi despre modificatorul volatil. Modificatorul volatil garantează doar că variabila va fi citită și scrisă în siguranță. Nu garantează că va fi schimbată în siguranță."

"Care este diferența?"

„Uitați-vă cum se modifică o variabilă:”

Cod Ce se întâmplă cu adevărat: Descriere
count++
register = count;

register = register+1;

count = register;
Pasul 1.
Valoarea variabilei este copiată din memoria globală într-un registru al procesorului.

Pasul 2.
În interiorul procesorului, variabila registrului este incrementată cu 1.

Pasul 3.
Valoarea variabilei este copiată de pe procesor în memoria globală.

"Wow! Deci, toate variabilele sunt modificate doar în procesor?"

"Da."

„Și valorile sunt copiate înainte și înapoi: din memorie la procesor și înapoi?”

"Da."

„Modificatorul volatil garantează că atunci când este accesat numărul de variabile, acesta va fi citit din memorie (pasul 1). Și dacă un fir dorește să atribuie o nouă valoare, va fi cu siguranță în memoria globală (pasul 3).”

„Dar mașina Java nu garantează că nu va exista nicio comutare între pașii 1 și 3.”

„Deci, creșterea variabilei cu 1 înseamnă de fapt trei operații?”

"Da."

„Și dacă două fire simultan vor să execute count++, atunci ar putea interfera între ele?”

"Da, verifică:"

Firma 1 Firma 2 Rezultat
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

„Deci, puteți accesa variabila, dar schimbarea acesteia este încă riscantă?”

„Ei bine, îl poți schimba, doar fii atent ☺”

"Cum?"

" sincronizat  este cel mai bun prieten al nostru."

"Înțeleg."