Uvod v sinhronizacijo na Javi

Sinhronizacija je funkcija Java, ki omejuje več niti, da bi hkrati iskali dostop do pogosto skupnih virov. Tu se skupni viri nanašajo na vsebino zunanjih datotek, spremenljivke razreda ali zapise baz podatkov.

Sinhronizacija se široko uporablja pri večkratnem programiranju. "Sinhronizirano" je ključna beseda, ki vaši kodi omogoča, da v njej omogoči, da na njej deluje samo ena nit, ne da bi v tem obdobju posegala katera koli druga nit.

Zakaj potrebujemo Sinhronizacijo na Javi?

  • Java je večnamenski programski jezik. To pomeni, da lahko dve ali več niti hkrati tečeta proti zaključku naloge. Ko se niti izvajajo hkrati, obstaja velika verjetnost, da se bo zgodil scenarij, kjer bi lahko koda zagotovila nepričakovane izide.
  • Morda se sprašujete, da če lahko večkratno branje povzroči napačne izhode, zakaj potem to velja za pomembno funkcijo na Javi?
  • Multithreading omogoča hitrejšo kodo z izvajanjem več niti vzporedno in s tem skrajša čas izvedbe kod in zagotavlja visoko zmogljivost. Vendar pa uporaba večreznega okolja vodi do napačnih izhodov zaradi stanja, ki je splošno znano kot dirkaško stanje.

Kaj je dirkaško stanje?

Ko dva ali več niti teče vzporedno, imajo dostop do in v tem času spreminjajo vire v skupni rabi. Zaporedja, v katerih se izvajajo niti, določi algoritem načrtovanja niti.

Zaradi tega ni mogoče predvideti vrstnega reda, v katerem se bodo izvajale niti, saj ga izključno upravlja načrtovalnik niti. To vpliva na izhod kode in povzroči neskladne izhode. Ker več niti poteka dirkajo med seboj za dokončanje operacije, se ta pogoj imenuje "dirkaško stanje".

Na primer, upoštevajmo spodnjo kodo:

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();
)
)

Ob zaporednem izvajanju zgornje kode bodo rezultati naslednji:

Naš vhod1:

Trenutni navoj se izvaja nit 1 Trenutna vrednost navoja 3

Trenutni navoj se izvaja nit 3 Trenutna vrednost navoja 2

Trenutna nit se izvaja nit 2 Trenutna vrednost navoja 3

Izhod2:

Trenutna nit se izvaja nit 3 Trenutna vrednost navoja 3

Trenutna nit se izvaja nit 2 Trenutna vrednost navoja 3

Trenutni navoj se izvaja nit 1 Trenutna vrednost navoja 3

Izhod3:

Trenutna nit se izvaja nit 2 Trenutna vrednost navoja 3

Trenutni navoj se izvaja nit 1 Trenutna vrednost navoja 3

Trenutna nit se izvaja nit 3 Trenutna vrednost navoja 3

Rezultat4:

Trenutna nit se izvaja nit 1 Trenutna vrednost navoja 2

Trenutna nit se izvaja nit 3 Trenutna vrednost navoja 3

Trenutni navoj se izvaja nit 2 Trenutna vrednost navoja 2

  • Iz zgornjega primera lahko ugotovite, da se niti izvajajo naključno in tudi vrednost je napačna. V skladu z našo logiko je treba vrednost povečati za 1. Vendar je v tem primeru izhodna vrednost v večini primerov 3, v nekaterih primerih pa 2.
  • Tu je spremenljivka "myVar" deljeni vir, na katerem se izvaja več niti. Niti dostopajo in spreminjajo vrednost "myVar" hkrati. Poglejmo, kaj se zgodi, če komentiramo drugi dve temi.

Rezultat v tem primeru je:

Trenutna nit, ki se izvaja nit 1 Trenutna vrednost navoja 1

To pomeni, da, ko en sam niz teče, je izhod po pričakovanjih. Ko pa teče več niti, vrednost spreminja vsak nit. Zato je treba omejiti število niti, ki delajo v skupnem viru, na en sam niz. To dosežemo s sinhronizacijo.

Razumevanje, kaj je sinhronizacija v Javi

  • Sinhronizacija v Javi se doseže s pomočjo ključne besede "sinhronizirano". Ta ključna beseda se lahko uporablja za metode ali bloke ali predmete, vendar je ni mogoče uporabiti z razredi in spremenljivkami. Sinhronizirani del kode omogoča dostop do ene samo nitke in njeno spreminjanje v določenem času.
  • Vendar sinhroniziran del kode vpliva na delovanje kode, saj poveča čakalni čas drugih niti, ki poskušajo dostopati do njega. Torej bi morali del kode sinhronizirati le, če obstaja možnost, da pride do dirk. Če se nihče ne bi smel izogniti.

Kako sinhronizacija na Javi deluje notranje?

  • Notranjo sinhronizacijo v Javi izvajamo s pomočjo zaslona lock (poznanega tudi kot monitor). Vsak predmet Java ima svojo ključavnico. V sinhroniziranem bloku kode mora nit pridobiti ključavnico, preden lahko izvede določen blok kode. Ko nit pridobi ključavnico, lahko izvede delček kode.
  • Po zaključku izvršitve samodejno sprosti ključavnico. Če za sinhronizirano kodo potrebuje še ena nit, počaka, da trenutni navoj, ki deluje na njej, sprosti ključavnico. Za ta postopek pridobivanja in sprostitve ključavnic interno poskrbi Java-ov virtualni stroj. Program ni odgovoren za pridobitev in sprostitev ključavnic s nitjo. Preostale niti pa lahko hkrati izvajajo kateri koli drug nesinhroniziran del kode.

Naj sinhroniziramo prejšnji primer s sinhronizacijo kode znotraj metode izvajanja s sinhroniziranim blokom v razredu "Spremeni", kot je navedeno spodaj:

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());
)
)
)

Koda za razred RaceCondition ostane enaka. Zdaj pri zagonu kode je izhod naslednji:

Izhod1:

Trenutna nit, ki se izvaja nit 1 Trenutna vrednost navoja 1

Trenutna nit, ki se izvaja nit 2 Trenutna vrednost navoja 2

Trenutna nit, ki se izvaja nit 3 Trenutna vrednost navoja 3

Izhod2:

Trenutna nit, ki se izvaja nit 1 Trenutna vrednost navoja 1

Trenutna nit, ki se izvaja nit 3 Trenutna vrednost navoja 2

Trenutna nit, ki se izvaja nit 2 Trenutna vrednost navoja 3

Upoštevajte, da naša koda zagotavlja pričakovani rezultat. Tu se vsaka nit poveča za 1 za spremenljivko "myVar" (v razredu "Modify").

Opomba: Sinhronizacija je potrebna, če na isti objekt deluje več niti. Če na več predmetov deluje več niti, sinhronizacija ni potrebna.

Na primer, spremenimo kodo v razredu "RaceCondition", kot je navedeno spodaj, in delamo s prej nesinhroniziranim razredom "Spremeni".

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();
)
)

Izhod:

Trenutna nit, ki se izvaja nit 1 Trenutna vrednost navoja 1

Trenutna nit, ki se izvaja nit 2 Trenutna vrednost navoja 1

Trenutna nit, ki se izvaja nit 3 Trenutna vrednost navoja 1

Vrste sinhronizacije na Javi:

Obstajata dve vrsti sinhronizacij niti, ena je medsebojno izključujoča, druga pa medpotezna komunikacija.

1. Vzajemno izključujoče

  • Sinhronizirana metoda.
  • Statična sinhronizirana metoda
  • Sinhronizirani blok.

2. Koordinacija niti (komunikacija med nitmi v javi)

Medsebojno izključujeta:

  • V tem primeru niti pridobijo ključavnico pred delovanjem na predmetu, s čimer se izognejo delu s predmeti, za katere so njihove vrednosti manipulirale z drugimi niti.
  • To lahko dosežemo na tri načine:

jaz. Sinhronizirana metoda: Za metodo lahko uporabimo ključno besedo "sinhronizirano" in s tem postane sinhronizirana metoda. Vsaka nit, ki prikliče sinhronizirano metodo, pridobi ključavnico za ta objekt in jo sprosti, ko je postopek končan. V zgornjem primeru lahko naredimo naš način "run ()" kot sinhroniziran s pomočjo ključne besede "sinhronizirano" po modifikatorju dostopa.

@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());
)

Izhod za ta primer bo:

Trenutna nit, ki se izvaja nit 1 Trenutna vrednost navoja 1

Trenutna nit, ki se izvaja nit 3 Trenutna vrednost navoja 2

Trenutna nit, ki se izvaja nit 2 Trenutna vrednost navoja 3

ii. Statična sinhronizirana metoda: Za sinhronizacijo statičnih metod je treba pridobiti zaklep na ravni razreda. Ko nit pridobi ključavnico na ravni razreda, potem bo lahko izvedla statično metodo. Medtem ko nit drži ključavnico ravni razreda, nobena druga nit ne more izvesti nobene druge statične sinhronizirane metode tega razreda. Vendar pa lahko druge niti izvajajo katero koli drugo redno metodo ali redno statično metodo ali celo nestetično sinhronizirano metodo tega razreda.

Na primer, razmislimo o našem razredu "Spremeni" in ga spremenimo tako, da pretvorimo naš način "prirastka" v statično sinhronizirano metodo. Spremembe kode so naslednje:

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. Sinhronizirani blok: Ena glavnih pomanjkljivosti sinhronizirane metode je, da povečuje čas čakanja na niti, kar vpliva na delovanje kode. Zato lahko za sinhronizacijo samo zahtevanih vrstic kode namesto celotne metode uporabimo sinhronizirani blok. Uporaba sinhroniziranega bloka zmanjša čas čakanja niti in izboljša tudi delovanje. V prejšnjem primeru smo že uporabili sinhronizirani blok, ko smo prvič sinhronizirali kodo.

Primer:
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());
)
)

Koordinacija navoja:

Za sinhronizirane niti je komunikacija med nitmi pomembna naloga. Vgrajene metode, ki pomagajo doseči komunikacijo med nitmi za sinhronizirano kodo, so:

  • počakaj ()
  • obvestiti()
  • notifyAll ()

Opomba: Te metode pripadajo objektnemu razredu in ne razredu niti. Da bi nit lahko priklicala te metode na predmet, bi morala držati ključavnico na tem predmetu. Prav tako te metode povzročijo, da nit sprosti ključavnico na predmetu, na katerega se prikliče.

wait (): nit pri priklicu metode wait () sprosti zaklep na predmetu in preide v stanje čakanja. Ima dve metodi preobremenitve:

  • javno končno neveljavno čakanje () vrže InterruptException
  • javno končno neveljavno čakanje (dolga časovna omejitev) vrže InterruptException
  • javno končno neveljavno čakanje (long timeout, int nanos) vrže InterruptException

notify (): nit pošlje signal v drugo nit v čakalnem stanju s pomočjo metode notify (). Obvestilo pošlje samo eni niti, tako da lahko ta nit nadaljuje z izvajanjem. Katera nit bo prejela obvestilo med vsemi niti v čakalnem stanju, je odvisno od navideznega stroja Java.

  • javno končno nično obvestilo ()

notifyAll (): Ko nit prikliče metodo notifyAll (), se obvesti vsa nit v čakalnem stanju. Te niti se bodo izvajale ena za drugo na podlagi vrstnega reda, ki ga je določil Java Virtual Machine.

  • javno končno nično obvestiloAll ()

Zaključek

V tem članku smo videli, kako lahko delo v več nitnem okolju povzroči neskladnost podatkov zaradi dirkaškega stanja. Kako nam sinhronizacija pomaga preseči to, da naenkrat omejimo eno samo nit, ki deluje na skupnem viru. Tudi kako sinhronizirane niti komunicirajo med seboj.

Priporočeni članki:

To je vodnik za Kaj je sinhronizacija na Javi ?. Tukaj razpravljamo o uvajanju, razumevanju, potrebah, delu in vrstah sinhronizacije z nekaj vzorčne kode. Če želite izvedeti več, lahko preberete tudi druge naše predlagane članke -

  1. Serializacija v Javi
  2. Kaj je generika v Javi?
  3. Kaj je API v Javi?
  4. Kaj je binarno drevo na Javi?
  5. Primeri in delovanje generike v C #