CodeGym /Curs Java /Modulul 2: Java Core /Firele nu sunt independente! Sincronizat!

Firele nu sunt independente! Sincronizat!

Modulul 2: Java Core
Nivel , Lecţie
Disponibil

"Bună, Amigo! Avem un panaceu - un leac pentru toate bolile. După cum am văzut deja, schimbarea necontrolată a firelor este o problemă."

„De ce firele în sine nu pot decide când să treacă la următorul fir? Face tot ce trebuie să facă și apoi semnalează „Am terminat!”?

„Permiterea firelor de execuție să controleze comutarea ar fi o problemă și mai mare. Să presupunem că aveți un cod scris prost, iar firul de execuție nu predă niciodată CPU-ul. Pe vremuri, așa funcționau. Și a fost un coșmar”.

"Bine. Deci, care este soluția?"

Blocarea altor fire.  Acesta este modul în care funcționează.”

A devenit clar că firele de execuție interferează unele cu altele atunci când încearcă să folosească obiecte și/sau resurse partajate . Așa cum am văzut în exemplul cu ieșirea consolei: există o singură consolă și toate firele de execuție ieșite la ea. E dezordonat.

Așa că a fost inventat un obiect special: mutexul . Este ca un semn pe ușa unei băi pe care scrie „disponibil / ocupat” . Are două stări: obiectul este disponibil sau ocupat . Aceste stări mai sunt numite „blocat” și „deblocat”.

Când un fir are nevoie de un obiect partajat cu alte fire, verifică mutex-ul asociat cu obiectul. Dacă mutex-ul este deblocat, atunci firul îl blochează (îl marchează ca „ocupat”) și începe să folosească resursa partajată. După ce firul și-a făcut treaba, mutex-ul este deblocat (marcat ca „disponibil”).

Dacă firul de execuție dorește să folosească obiectul și mutex-ul este blocat, atunci firul de execuție va fi oprit în timp ce așteaptă. Când mutex-ul este în sfârșit deblocat de firul ocupant, firul nostru îl va bloca imediat și va începe să ruleze. Analogia cu un semn de ușă de baie este perfectă.

"Deci, cum lucrez cu un mutex? Trebuie să creez obiecte speciale?"

"Este mult mai simplu decât atât. Creatorii Java au integrat acest mutex în clasa Object. Deci nici nu trebuie să-l creați. Face parte din fiecare obiect. Iată cum funcționează totul:"

Cod Descriere
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Metoda swap schimbă valorile variabilelor nume1 și nume2.

Ce s-ar putea întâmpla dacă este apelat din două fire în același timp?

Execuția reală a codului Codul primului fir Codul celui de-al doilea fir
String s1 = name1; //Ally
name1 = name2; //Lena
name2 = s1; //Ally

String s2 = name1; //Lena
name1 = name2; //Ally
name2 = s2; //Lena
String s1 = name1;
name1 = name2;
//other thread is running
name2 = s1;
//the thread waits until the mutex is unlocked

String s2 = name1;
name1 = name2;
//other thread is running
//other thread is running
name2 = s2;
Linia de jos
Valorile variabilelor au fost schimbate de două ori, revenind la locurile inițiale.

Acordați atenție cuvântului cheie  sincronizat .

— Da, ce înseamnă?

„Când un fir de execuție intră într-un bloc de cod marcat ca sincronizat, mașina Java blochează imediat mutex-ul obiectului indicat în paranteze după cuvântul sincronizat. Niciun alt thread nu poate intra în acest bloc până când firul nostru îl părăsește. De îndată ce firul nostru pleacă. blocul marcat sincronizat, mutex-ul este deblocat imediat și automat și va fi disponibil pentru a fi achiziționat de un alt thread."

Dacă mutex-ul este ocupat, atunci firul nostru va sta nemișcat și va aștepta să se elibereze.

"Atât de simplu și atât de elegant. Asta este o soluție frumoasă."

— Da. Dar ce crezi că se va întâmpla în acest caz?

Cod Descriere
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public void swap2()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Metodele swap și swap2 au același mutex ( obiectul this ).

Ce se întâmplă dacă un fir apelează metoda swap și un alt fir apelează metoda swap2?

"Deoarece mutex-ul este același, al doilea thread va trebui să aștepte până când primul thread părăsește blocul sincronizat. Deci nu vor fi probleme cu accesul simultan."

"Bravo, Amigo! Acesta este răspunsul corect!"

Acum aș dori să subliniez că sincronizarea poate fi folosită pentru a marca nu numai blocuri de cod, ci și metode. Iată ce înseamnă asta:

Cod Ce se întâmplă cu adevărat
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

public synchronized void swap()
{
String s = name1;
name1 = name2;
name2 = s;
}

public static synchronized void swap2()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public static void swap2()
{
synchronized (MyClass.class)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION