Introduksjon til synkronisering i Java

Synkronisering er en Java-funksjon som begrenser flere tråder fra å prøve å få tilgang til de felles delte ressursene samtidig. Her refererte delte ressurser til eksternt filinnhold, klassevariabler eller databaseposter.

Synkronisering er mye brukt i multetrådprogrammering. "Synkronisert" er nøkkelordet som gir koden din muligheten til å tillate bare en enkelt tråd å operere på den uten forstyrrelser fra andre tråder i løpet av den perioden.

Hvorfor trenger vi synkronisering i Java?

  • Java er et flertrinns programmeringsspråk. Dette betyr at to eller flere tråder kan løpe samtidig mot fullføringen av en oppgave. Når tråder kjøres samtidig, er det store sjanser for at det oppstår et scenario der koden din kan gi uventede utfall.
  • Du lurer kanskje på at hvis multithreading kan føre til feil utganger, hvorfor blir det da ansett som en viktig funksjon i Java?
  • Multitretting gjør koden raskere ved å kjøre flere tråder parallelt og dermed redusere kodetidens utførelsestid og gi høy ytelse. Å benytte seg av multetrådmiljøet fører imidlertid til unøyaktige utganger på grunn av en tilstand som vanligvis er kjent som en rasetilstand.

Hva er en rase-tilstand?

Når to eller flere tråder løper parallelt, har de en tendens til å få tilgang til og endre delte ressurser på det tidspunktet. Sekvensene som trådene blir kjørt i blir bestemt av trådplanleggingsalgoritmen.

På grunn av dette kan man ikke forutsi rekkefølgen av tråder som skal utføres, da de kun styres av trådplanleggeren. Dette påvirker output av koden og resulterer i inkonsekvente utganger. Siden flere tråder kappes med hverandre for å fullføre operasjonen, blir tilstanden referert til som "racingtilstand".

La oss for eksempel vurdere koden nedenfor:

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

Når du kjører ovenstående kode fortløpende, vil utgangene være som følger:

Ourput1:

Gjeldende tråd som blir utført tråd 1 Gjeldende trådverdi 3

Gjeldende tråd blir utført tråd 3 Gjeldende trådverdi 2

Gjeldende tråd blir utført tråd 2 Gjeldende trådverdi 3

Output2:

Gjeldende tråd blir utført tråd 3 Gjeldende trådverdi 3

Gjeldende tråd blir utført tråd 2 Gjeldende trådverdi 3

Gjeldende tråd som blir utført tråd 1 Gjeldende trådverdi 3

Output3:

Gjeldende tråd blir utført tråd 2 Gjeldende trådverdi 3

Gjeldende tråd som blir utført tråd 1 Gjeldende trådverdi 3

Gjeldende tråd blir utført tråd 3 Gjeldende trådverdi 3

Output4:

Gjeldende tråd som blir utført tråd 1 Gjeldende trådverdi 2

Gjeldende tråd blir utført tråd 3 Gjeldende trådverdi 3

Gjeldende tråd blir utført tråd 2 Gjeldende trådverdi 2

  • Fra eksemplet over kan du konkludere med at trådene blir utført tilfeldig, og at verdien også er feil. I henhold til vår logikk, bør verdien økes med 1. Imidlertid er outputverdien i de fleste tilfeller 3 og i noen få tilfeller er den 2.
  • Her er "myVar" -variabelen den delte ressursen som flere tråder kjører på. Trådene får tilgang til og endrer verdien av “myVar” samtidig. La oss se hva som skjer hvis vi kommenterer de to andre trådene.

Utgangen i dette tilfellet er:

Gjeldende tråd som utføres tråd 1 Gjeldende trådverdi 1

Dette betyr at når en enkelt tråd kjører, er utgangen som forventet. Imidlertid, når flere tråder kjører, endres verdien av hver tråd. Derfor må man begrense antall tråder som arbeider på en delt ressurs, til en enkelt tråd om gangen. Dette oppnås ved hjelp av synkronisering.

Forstå hva som er synkronisering i Java

  • Synkronisering i Java oppnås ved hjelp av nøkkelordet "synkronisert". Dette nøkkelordet kan brukes til metoder eller blokker eller objekter, men kan ikke brukes med klasser og variabler. Et synkronisert kodestykke lar bare en tråd få tilgang til og endre den på et gitt tidspunkt.
  • Imidlertid påvirker et synkronisert stykke kode kodens ytelse da det øker ventetiden for andre tråder som prøver å få tilgang til den. Så et kodestykke skal bare synkroniseres når det er en sjanse for at en løpstilstand oppstår. Hvis ikke bør man unngå det.

Hvordan fungerer synkronisering i Java internt?

  • Intern synkronisering i Java har implementert ved hjelp av låsekonsept (også kjent som en monitor). Hvert Java-objekt har sin egen lås. I en synkronisert kodeblokk trenger en tråd å skaffe seg låsen før han kan utføre den bestemte koden. Når en tråd har fått låsen, kan den utføre det koden.
  • Når fullføringen er fullført, slipper den automatisk låsen. Hvis en annen tråd krever å operere på den synkroniserte koden, venter den på at den nåværende tråden som opererer på den, skal løsne låsen. Denne prosessen med å skaffe og slippe låser blir internt ivaretatt av den virtuelle Java-maskinen. Et program er ikke ansvarlig for å skaffe og slippe låser av tråden. De resterende trådene kan imidlertid utføre alle andre ikke-synkroniserte kodestykker samtidig.

La oss synkronisere vårt forrige eksempel ved å synkronisere koden i kjøremetoden ved å bruke den synkroniserte blokken i klassen “Endre” som nedenfor:

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

Koden for klassen “RaceCondition” forblir den samme. Nå som koden kjøres, er utgangen som følger:

Output1:

Gjeldende tråd som utføres tråd 1 Gjeldende trådverdi 1

Gjeldende tråd som blir kjørt, tråd 2 Gjeldende trådverdi 2

Gjeldende tråd som utføres tråd 3 Gjeldende trådverdi 3

Output2:

Gjeldende tråd som utføres tråd 1 Gjeldende trådverdi 1

Gjeldende tråd som blir kjørt, tråd 3 Gjeldende trådverdi 2

Gjeldende tråd som blir kjørt, tråd 2 Gjeldende trådverdi 3

Legg merke til at koden vår gir forventet effekt. Her øker hver tråd verdien med 1 for variabelen “myVar” (i klassen “Endre”).

Merk: Synkronisering er nødvendig når flere tråder opererer på samme objekt. Hvis flere tråder fungerer på flere objekter, er ikke synkronisering nødvendig.

La oss for eksempel endre koden i klassen “RaceCondition” som nedenfor og arbeide med den tidligere usynkroniserte klassen “Modify”.

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

Produksjon:

Gjeldende tråd som utføres tråd 1 Gjeldende trådverdi 1

Gjeldende tråd som utføres tråd 2 Gjeldende trådverdi 1

Gjeldende tråd som utføres tråd 3 Gjeldende trådverdi 1

Typer synkronisering i Java:

Det er to typer trådsynkronisering, den ene er gjensidig eksklusiv og den andre kommunikasjon mellom tråder.

1.Mutually Exclusive

  • Synkronisert metode.
  • Statisk synkronisert metode
  • Synkronisert blokk.

2. Tråd-koordinering (kommunikasjon mellom trådene i java)

Gjensidig utelukkende:

  • I dette tilfellet får tråder låsen før du bruker en gjenstand, og unngår å arbeide med objekter som har fått verdiene sine manipulert med andre tråder.
  • Dette kan oppnås på tre måter:

Jeg. Synkronisert metode: Vi kan bruke det "synkroniserte" nøkkelordet for en metode, og dermed gjøre det til en synkronisert metode. Hver tråd som påberoper den synkroniserte metoden, får tak i låsen for objektet og slipper den når operasjonen er fullført. I eksemplet ovenfor kan vi gjøre vår "run ()" -metode som synkronisert ved å bruke det "synkroniserte" nøkkelordet etter tilgangsmodifisereren.

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

Utdataene for dette tilfellet vil være:

Gjeldende tråd som utføres tråd 1 Gjeldende trådverdi 1

Gjeldende tråd som blir kjørt, tråd 3 Gjeldende trådverdi 2

Gjeldende tråd som blir kjørt, tråd 2 Gjeldende trådverdi 3

ii. Statisk synkronisert metode: For å synkronisere statiske metoder trenger man å skaffe seg klassens lås. Etter at en tråd bare oppnår klasselåsen, vil den kunne utføre en statisk metode. Mens en tråd har klasselåsen, kan ingen andre tråder utføre noen annen statisk synkronisert metode for den klassen. Imidlertid kan de andre trådene utføre enhver annen vanlig metode eller vanlig statisk metode eller til og med ikke-statisk synkronisert metode i den klassen.

La oss for eksempel vurdere klassen “Endre” og gjøre endringer i den ved å konvertere “økning” -metoden til en statisk synkronisert metode. Koden endringer er som nedenfor:

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. Synkronisert blokk: En av de største ulempene ved den synkroniserte metoden er at den øker trådens ventetid som påvirker kodenes ytelse. Derfor for å kunne synkronisere bare de nødvendige kodelinjene i stedet for hele metoden, trenger man å benytte seg av en synkronisert blokk. Bruk av synkronisert blokk reduserer ventetiden på trådene og forbedrer ytelsen også. I forrige eksempel har vi allerede benyttet oss av synkronisert blokk mens vi synkroniserte koden vår for første gang.

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

Trådkoordinering:

For synkroniserte tråder er kommunikasjon mellom tråder en viktig oppgave. Innebygde metoder som hjelper til med å oppnå kommunikasjon mellom trådene for synkronisert kode er nemlig:

  • vente()
  • gi beskjed()
  • notifyAll ()

Merk: Disse metodene tilhører objektklassen og ikke trådklassen. For at en tråd skal kunne påkalle disse metodene på et objekt, bør den holde låsen på det objektet. Disse metodene får også en tråd til å frigjøre låsen på gjenstanden den blir påkalt på.

vent (): En tråd på påkallende vent () -metode, slipper låsen på objektet og går i ventetilstand. Den har to metodebelastninger:

  • public final void wait () kaster InterruptException
  • offentlig endelig ugyldig ventetid (lang tidsavbrudd) kaster InterruptException
  • offentlig endelig ugyldig ventetid (lang timeout, int nanos) kaster InterruptException

varsle (): En tråd sender et signal til en annen tråd i ventetilstand ved å bruke varslingsmetoden. Den sender varselet til bare en tråd slik at denne tråden kan fortsette utførelsen. Hvilken tråd som vil motta varselet blant alle trådene i ventetilstand, avhenger av Java Virtual Machine.

  • offentlig endelig ugyldig melding ()

notifyAll (): Når en tråd påberoper notifyAll () -metoden, varsles hver tråd i sin ventetilstand. Disse trådene blir kjørt etter hverandre basert på bestillingen som er bestemt av Java Virtual Machine.

  • offentlig endelig ugyldig meldingAlle ()

Konklusjon

I denne artikkelen har vi sett hvordan det å jobbe i et flertrådet miljø kan føre til datakonsekvens på grunn av en rasetilstand. Hvordan synkronisering hjelper oss å overvinne dette ved å begrense en enkelt tråd til å operere på en delt ressurs om gangen. Også hvordan synkroniserte tråder kommuniserer med hverandre.

Anbefalte artikler:

Dette har vært en guide til Hva er synkronisering i Java ?. Her diskuterer vi introduksjon, forståelse, behov, arbeid og typer synkronisering med noen eksempelskoder. Du kan også gå gjennom andre foreslåtte artikler for å lære mer -

  1. Serialisering i Java
  2. Hva er Generics i Java?
  3. Hva er API i Java?
  4. Hva er et binærtre i Java?
  5. Eksempler og hvordan generikker fungerer i C #