Úvod do synchronizácie v jazyku Java

Synchronizácia je funkcia jazyka Java, ktorá obmedzuje prístup viacerých vlákien k pokusu o prístup k spoločne zdieľaným prostriedkom súčasne. Zdieľané zdroje sa týkajú externého obsahu súborov, premenných triedy alebo databázových záznamov.

Synchronizácia je široko používaná vo viacvláknovom programovaní. „Synchronizované“ je kľúčové slovo, ktoré vášmu kódu umožňuje povoliť na ňom pracovať iba jedno vlákno bez rušenia iným vláknom počas tohto obdobia.

Prečo potrebujeme synchronizáciu v jazyku Java?

  • Java je viacvláknový programovací jazyk. To znamená, že k dokončeniu úlohy môžu bežať súčasne dve alebo viac vlákien. Keď vlákna prebiehajú súčasne, existuje veľká pravdepodobnosť, že dôjde k situácii, keď váš kód môže poskytnúť neočakávané výsledky.
  • Možno vás zaujíma, že ak multithreading môže spôsobiť chybné výstupy, tak prečo sa považuje za dôležitú funkciu v Jave?
  • Vďaka multithreadingu je váš kód rýchlejší tým, že paralelne spúšťa viacero vlákien, čím sa skracuje čas vykonávania kódu a poskytuje vysoký výkon. Využitie prostredia s viacerými vláknami však vedie k nepresným výstupom v dôsledku stavu všeobecne známeho ako rasový stav.

Čo je to Race Race?

Ak sú dve alebo viac vlákien spustené paralelne, majú v tom čase tendenciu pristupovať k zdieľaným zdrojom a upravovať ich. O postupnosti, v ktorej sa vlákna vykonajú, sa rozhodne algoritmus plánovania vlákien.

Z tohto dôvodu nie je možné predpovedať poradie, v ktorom sa budú vlákna vykonávať, pretože je riadené výlučne plánovačom vlákien. Toto ovplyvňuje výstup kódu a vedie k nekonzistentným výstupom. Pretože na dokončenie operácie spolu preteká viacero vlákien, stav sa označuje ako „závodný stav“.

Napríklad zvážme nasledujúci kód:

Class Modify:
package JavaConcepts;
public class Modify implements Runnable(
private int myVar=0;
public int getMyVar() (
return myVar;
)
public void setMyVar(int myVar) (
this.myVar = myVar;
)
public void increment() (
myVar++;
)
@Override
public void run() (
// TODO Auto-generated method stub
this.increment();
System.out.println("Current thread being executed "+ Thread.currentThread().getName() + "Current Thread value " + this.getMyVar());
)
)
Class RaceCondition:
package JavaConcepts;
public class RaceCondition (
public static void main(String() args) (
Modify mObj = new Modify();
Thread t1 = new Thread(mObj, "thread 1");
Thread t2 = new Thread(mObj, "thread 2");
Thread t3 = new Thread(mObj, "thread 3");
t1.start();
t2.start();
t3.start();
)
)

Pri postupnom spustení vyššie uvedeného kódu budú výstupy nasledujúce:

Ourput1:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 3

Aktuálne vlákno sa vykonáva vlákno 3 Aktuálna hodnota vlákna 2

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 3

výstup2:

Aktuálne vlákno sa vykonáva vlákno 3 Aktuálna hodnota vlákna 3

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 3

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 3

výstup3:

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 3

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 3

Aktuálne vlákno sa vykonáva vlákno 3 Aktuálna hodnota vlákna 3

VÝSTUP4:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 2

Aktuálne vlákno sa vykonáva vlákno 3 Aktuálna hodnota vlákna 3

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 2

  • Z vyššie uvedeného príkladu môžete vyvodiť záver, že vlákna sú vykonávané náhodne a tiež ich hodnota je nesprávna. Podľa našej logiky by sa hodnota mala zvýšiť o 1. Avšak tu je výstupná hodnota vo väčšine prípadov 3 a v niektorých prípadoch 2.
  • Premenná „myVar“ je tu zdieľaný prostriedok, na ktorom sa spúšťa viacero vlákien. Vlákna pristupujú a modifikujú hodnotu „myVar“ súčasne. Pozrime sa, čo sa stane, ak komentujeme ďalšie dve vlákna.

Výstupom v tomto prípade je:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 1

To znamená, že keď beží jedno vlákno, výstup je očakávaný. Ak je však spustených viacero vlákien, hodnota sa mení každým vláknom. Preto je potrebné obmedziť počet vlákien pracujúcich na zdieľanom prostriedku naraz na jedno vlákno. To sa dosiahne pomocou synchronizácie.

Pochopenie Čo je synchronizácia v jazyku Java

  • Synchronizácia v Java sa dosiahne pomocou kľúčového slova „synchronized“. Toto kľúčové slovo sa môže použiť pre metódy alebo bloky alebo objekty, ale nemôže sa použiť s triedami a premennými. Synchronizovaná časť kódu umožňuje prístup a úpravu iba jedného vlákna v danom čase.
  • Synchronizovaný kus kódu však ovplyvňuje výkonnosť kódu, pretože zvyšuje čakaciu dobu ostatných vlákien, ktoré sa ho snažia získať. Časť kódu by sa preto mala synchronizovať iba vtedy, keď existuje šanca, že dôjde k stavu rasy. Ak tomu tak nie je, malo by sa tomu vyhnúť.

Ako interne funguje synchronizácia v jazyku Java?

  • Interná synchronizácia v jazyku Java bola implementovaná pomocou konceptu zámku (známeho tiež ako monitor). Každý objekt Java má svoj vlastný zámok. V synchronizovanom bloku kódu musí vlákno získať zámok skôr, ako bude môcť vykonať tento konkrétny blok kódu. Akonáhle vlákno získa zámok, môže vykonať tento kus kódu.
  • Po dokončení vykonávania automaticky zámok uvoľní. Ak si vyžaduje ďalšie vlákno, aby pracovalo na synchronizovanom kóde, čaká na uvoľnenie zámku pre aktuálne vlákno, ktoré na ňom pôsobí. Tento proces získavania a uvoľňovania zámkov interne zabezpečuje virtuálny stroj Java. Program nezodpovedá za získanie a uvoľnenie zámkov pomocou vlákna. Zostávajúce vlákna však môžu vykonávať akýkoľvek iný nesynchronizovaný kus kódu súčasne.

Zosynchronizujte náš predchádzajúci príklad synchronizáciou kódu vo vnútri metódy run pomocou synchronizovaného bloku v triede „Modify“, ako je uvedené nižšie:

Class Modify:
package JavaConcepts;
public class Modify implements Runnable(
private int myVar=0;
public int getMyVar() (
return myVar;
)
public void setMyVar(int myVar) (
this.myVar = myVar;
)
public void increment() (
myVar++;
)
@Override
public void run() (
// TODO Auto-generated method stub
synchronized(this) (
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
)
)
)

Kód triedy „RaceCondition“ zostáva rovnaký. Po spustení kódu je výstup nasledovný:

Output1:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 1

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 2

Aktuálne vlákno sa vykonáva vlákno 3 Hodnota aktuálneho vlákna 3

výstup2:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 1

Aktuálne vlákno sa vykonáva vlákno 3 Hodnota aktuálneho vlákna 2

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 3

Všimnite si, že náš kód poskytuje očakávaný výstup. Tu každé vlákno zvyšuje hodnotu o 1 pre premennú „myVar“ (v triede „Zmeniť“).

Poznámka: Synchronizácia je potrebná, keď na rovnakom objekte pracuje viac vlákien. Ak na viacerých objektoch pracuje viacero vlákien, synchronizácia sa nevyžaduje.

Napríklad upravme kód v triede „RaceCondition“, ako je uvedené nižšie, a pracujte s predtým nesynchronizovanou triedou „Upraviť“.

package JavaConcepts;
public class RaceCondition (
public static void main(String() args) (
Modify mObj = new Modify();
Modify mObj1 = new Modify();
Modify mObj2 = new Modify();
Thread t1 = new Thread(mObj, "thread 1");
Thread t2 = new Thread(mObj1, "thread 2");
Thread t3 = new Thread(mObj2, "thread 3");
t1.start();
t2.start();
t3.start();
)
)

Výkon:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 1

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 1

Aktuálne vlákno sa vykonáva vlákno 3 Hodnota aktuálneho vlákna 1

Typy synchronizácie v jazyku Java:

Existujú dva typy synchronizácie vlákien, jeden sa vzájomne vylučujú a druhý medzi vláknami.

1.Mutually Exclusive

  • Synchronizovaná metóda.
  • Statická synchronizovaná metóda
  • Synchronizovaný blok.

2.Celková koordinácia (komunikácia medzi vláknami v jave)

Vzájomne exkluzívne:

  • V tomto prípade vlákna získajú zámok predtým, ako začnú pracovať s objektom, čím sa vyhýba práci s objektmi, ktorých hodnoty boli upravené inými vláknami.
  • To možno dosiahnuť tromi spôsobmi:

i. Synchronizovaná metóda: Pre metódu môžeme použiť kľúčové slovo „synchronizované“, čím sa z nej stane synchronizovaná metóda. Každé vlákno, ktoré vyvoláva synchronizovanú metódu, získa zámok pre tento objekt a uvoľní ho po dokončení jeho operácie. V uvedenom príklade môžeme našu metódu „run ()“ synchronizovať pomocou kľúčového slova „synchronized“ po modifikátore prístupu.

@Override
public synchronized void run() (
// TODO Auto-generated method stub
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
)

Výstupom pre tento prípad bude:

Aktuálne vlákno sa vykonáva vlákno 1 Hodnota aktuálneho vlákna 1

Aktuálne vlákno sa vykonáva vlákno 3 Hodnota aktuálneho vlákna 2

Aktuálne vlákno sa vykonáva vlákno 2 Hodnota aktuálneho vlákna 3

ii. Statická synchronizovaná metóda: Na synchronizáciu statických metód je potrebné získať zámok na úrovni triedy. Keď vlákno získa zámok na úrovni triedy, potom bude môcť vykonať statickú metódu. Zatiaľ čo vlákno drží zámok na úrovni triedy, žiadne iné vlákno nemôže vykonať žiadnu inú statickú synchronizovanú metódu tejto triedy. Ostatné vlákna však môžu vykonávať akúkoľvek inú pravidelnú metódu alebo bežnú statickú metódu alebo dokonca nestatickú synchronizovanú metódu tejto triedy.

Uvažujme napríklad našu triedu „Zmeniť“ a vykonajte zmeny v nej prevedením našej metódy „prírastku“ na statickú synchronizovanú metódu. Zmeny kódu sú uvedené nižšie:

package JavaConcepts;
public class Modify implements Runnable(
private static int myVar=0;
public int getMyVar() (
return myVar;
)
public void setMyVar(int myVar) (
this.myVar = myVar;
)
public static synchronized void increment() (
myVar++;
System.out.println("Current thread being executed " + Thread.currentThread().getName() + " Current Thread value " + myVar);
)
@Override
public void run() (
// TODO Auto-generated method stub
increment();
)
)

iii. Synchronizovaný blok: Jednou z hlavných nevýhod synchronizovanej metódy je to, že zvyšuje čakaciu dobu nití ovplyvňujúcich výkon kódu. Preto, aby bolo možné synchronizovať iba požadované riadky kódu namiesto celej metódy, je potrebné využiť synchronizovaný blok. Použitie synchronizovaného bloku znižuje čakaciu dobu vlákien a zvyšuje výkon. V predchádzajúcom príklade sme už použili synchronizovaný blok pri prvej synchronizácii nášho kódu.

Príklad:
public void run() (
// TODO Auto-generated method stub
synchronized(this) (
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
)
)

Koordinácia vlákna:

Pre synchronizované vlákna je dôležitá úloha komunikácia medzi vláknami. Vstavané metódy, ktoré pomáhajú dosiahnuť komunikáciu medzi vláknami pre synchronizovaný kód, sú najmä:

  • počkať ()
  • oznámiť ()
  • notifyAll ()

Poznámka: Tieto metódy patria do triedy objektov a nie do triedy vlákien. Aby vlákno mohlo vyvolať tieto metódy na objekte, malo by držať zámok na tomto objekte. Tieto metódy tiež spôsobujú, že vlákno uvoľní zámok na objekte, na ktorý je vyvolané.

wait (): Vlákno pri vyvolaní metódy wait (), uvoľní zámok na objekte a prejde do čakacieho stavu. Má dve preťaženia metódami:

  • verejné posledné neplatné čakanie () vyvolá prerušenú výnimku
  • verejné posledné prázdne čakanie (dlhý časový limit) vyvolá prerušenú výnimku
  • verejné konečné prázdne čakanie (dlhý časový limit, int nanos) vyvolá prerušenú výnimku

notify (): Vlákno vyšle signál do iného vlákna v čakajúcom stave pomocou metódy Notify (). Oznámenie odošle iba jednému vláknu, takže toto vlákno môže pokračovať v jeho vykonávaní. Ktoré vlákno dostane oznámenie medzi všetkými vláknami v čakajúcom stave, závisí od Java Virtual Machine.

  • verejné konečné oznámenie neplatnosti ()

notifyAll (): Keď vlákno vyvolá metódu announAll (), upozorní sa každé vlákno v čakajúcom stave. Tieto vlákna sa vykonajú jeden po druhom na základe poradia, o ktorom rozhodne Java Virtual Machine.

  • verejné konečné vyhlásenie neplatnostiVšetky ()

záver

V tomto článku sme videli, ako práca v prostredí s viacerými vláknami môže viesť k nekonzistencii údajov v dôsledku rasového stavu. Ako nám synchronizácia pomáha prekonať tento problém obmedzením jediného vlákna na prácu na zdieľanom prostriedku súčasne. Tiež to, ako synchronizované vlákna spolu komunikujú.

Odporúčané články:

Toto bola príručka Čo je synchronizácia v jazyku Java ?. Tu diskutujeme o úvodu, porozumení, potrebe, práci a typoch synchronizácie s ukážkovým kódom. Viac informácií nájdete aj v ďalších navrhovaných článkoch -

  1. Serializácia v Jave
  2. Čo je generika v jazyku Java?
  3. Čo je API v Java?
  4. Čo je binárny strom v Jave?
  5. Príklady a ako generiká fungujú v jazyku C #

Kategórie: