Introduktion till synkronisering i Java

Synkronisering är en Java-funktion som begränsar flera trådar från att försöka komma åt de vanligt delade resurserna samtidigt. Här delade resurser hänvisar till externt filinnehåll, klassvariabler eller databasposter.

Synkronisering används ofta i multitrådad programmering. "Synkroniserad" är nyckelordet som ger din kod möjlighet att bara tillåta en enda tråd att fungera på den utan störningar från någon annan tråd under den perioden.

Varför behöver vi synkronisering i Java?

  • Java är ett flertrådigt programmeringsspråk. Detta betyder att två eller flera trådar kan köras samtidigt mot slutförandet av en uppgift. När trådar körs samtidigt finns det stora chanser att ett scenario inträffar där din kod kan ge oväntade resultat.
  • Du kanske undrar att om multithreading kan orsaka felaktiga output, varför anses det därför vara en viktig funktion i Java?
  • Multitrådning gör din kod snabbare genom att köra flera trådar parallellt och därmed minska din kodkörningstid och ge hög prestanda. Att använda multithreading-miljön leder emellertid till felaktiga utgångar på grund av ett tillstånd som allmänt kallas rasförhållanden.

Vad är ett rasvillkor?

När två eller flera trådar körs parallellt tenderar de att komma åt och ändra delade resurser vid den tidpunkten. Sekvenserna i vilka trådarna körs avgörs av trådplaneringsalgoritmen.

På grund av detta kan man inte förutsäga ordningen i vilka trådar kommer att köras eftersom de styrs endast av trådplaneraren. Detta påverkar utdata från koden och resulterar i inkonsekventa utgångar. Eftersom flera trådar tävlar med varandra för att slutföra operationen benämns villkoret ”racingvillkor”.

Låt oss till exempel ta hänsyn till koden nedan:

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 kör ovanstående kod i följd kommer utgångarna att vara följande:

Ourput1:

Aktuell tråd som körs tråd 1 Aktuell trådvärde 3

Aktuell tråd som körs tråd 3 Aktuell trådvärde 2

Aktuell tråd som körs tråd 2 Aktuell trådvärde 3

Output2:

Aktuell tråd som körs tråd 3 Aktuell trådvärde 3

Aktuell tråd som körs tråd 2 Aktuell trådvärde 3

Aktuell tråd som körs tråd 1 Aktuell trådvärde 3

UTDATA 3:

Aktuell tråd som körs tråd 2 Aktuell trådvärde 3

Aktuell tråd som körs tråd 1 Aktuell trådvärde 3

Aktuell tråd som körs tråd 3 Aktuell trådvärde 3

Output4:

Aktuell tråd som körs tråd 1 Aktuell trådvärde 2

Aktuell tråd som körs tråd 3 Aktuell trådvärde 3

Aktuell tråd som körs tråd 2 Aktuell trådvärde 2

  • Från exemplet ovan kan du dra slutsatsen att trådarna körs slumpmässigt och att värdet är felaktigt. Enligt vår logik bör värdet ökas med 1. Men här är utgångsvärdet i de flesta fall 3 och i några få fall är det 2.
  • Här är "myVar" -variabeln den delade resursen som flera trådar kör på. Trådarna kommer åt och ändrar värdet på "myVar" samtidigt. Låt oss se vad som händer om vi kommenterar de andra två trådarna.

Utgången i detta fall är:

Den aktuella tråden som körs tråd 1 Aktuell trådvärde 1

Detta betyder att när en enda tråd kör, är utgången som förväntat. Men när flera trådar körs ändras värdet av varje tråd. Därför måste man begränsa antalet trådar som arbetar på en delad resurs till en enda tråd åt gången. Detta uppnås med synkronisering.

Förstå vad som är synkronisering i Java

  • Synkronisering i Java uppnås med hjälp av nyckelordet "synkroniserad". Detta nyckelord kan användas för metoder eller block eller objekt men kan inte användas med klasser och variabler. Ett synkroniserat kodstycke tillåter bara en tråd att komma åt och ändra den vid en viss tidpunkt.
  • Men en synkroniserad kodbit påverkar kodens prestanda eftersom den ökar väntetiden för andra trådar som försöker få åtkomst till den. Så en kodkod ska bara synkroniseras när det finns en chans för ett tävlingsförhållande. Om inte ska man undvika det.

Hur fungerar synkronisering i Java internt?

  • Intern synkronisering i Java har implementerats med hjälp av lås (även känt som en monitor) -koncept. Varje Java-objekt har sitt eget lås. I ett synkroniserat kodblock måste en tråd anskaffa låset innan han kan köra det specifika kodblocket. När en tråd förvärvar låset kan den köra kodstycket.
  • Efter genomförandet släpper den automatiskt låset. Om en annan tråd kräver att hantera den synkroniserade koden, väntar den på att den aktuella tråden som fungerar på den släpper låset. Denna process för att förvärva och frigöra lås tas internt hand om av den virtuella Java-maskinen. Ett program ansvarar inte för att förvärva och frigöra lås med tråden. De återstående trådarna kan emellertid köra alla andra icke-synkroniserade kodstycken samtidigt.

Låt oss synkronisera vårt tidigare exempel genom att synkronisera koden i körmetoden med hjälp av det synkroniserade blocket i klassen "Ändra" enligt nedan:

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 för klassen "RaceCondition" förblir densamma. Nu när koden körs är utgången enligt följande:

Utgång 1:

Den aktuella tråden som körs tråd 1 Aktuell trådvärde 1

Den aktuella tråden som körs tråd 2 Aktuell trådvärde 2

Den aktuella tråden som körs tråd 3 Aktuell trådvärdet 3

Output2:

Den aktuella tråden som körs tråd 1 Aktuell trådvärde 1

Den aktuella tråden som körs tråd 3 Aktuell trådvärde 2

Den aktuella tråden som körs tråd 2 Aktuell trådvärdet 3

Observera att vår kod tillhandahåller den förväntade utgången. Här ökar varje tråd värdet med 1 för variabeln "myVar" (i klassen "Ändra").

Obs: Synkronisering krävs när flera trådar arbetar med samma objekt. Om flera trådar fungerar på flera objekt krävs inte synkronisering.

Låt oss till exempel modifiera koden i klassen "RaceCondition" enligt nedan och arbeta med den tidigare osynkroniserade 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();
)
)

Produktion:

Den aktuella tråden som körs tråd 1 Aktuell trådvärde 1

Den aktuella tråden som körs tråd 2 Aktuell trådvärde 1

Den aktuella tråden som körs tråd 3 Aktuell trådvärde 1

Typer av synkronisering i Java:

Det finns två typer av trådsynkronisering, den ena är ömsesidigt exklusiv och den andra kommunikationen mellan trådarna.

1.Mutually exklusivt

  • Synkroniserad metod.
  • Statisk synkroniserad metod
  • Synkroniserat block.

2. Trådkoordination (kommunikation mellan trådar i java)

Ömsesidigt exklusivt:

  • I detta fall erhåller trådar låset innan man arbetar på ett objekt och undviker därmed att arbeta med föremål som har fått sina värden manipulerade av andra trådar.
  • Detta kan uppnås på tre sätt:

i. Synkroniserad metod: Vi kan använda det "synkroniserade" nyckelordet för en metod och därmed göra det till en synkroniserad metod. Varje tråd som åberopar den synkroniserade metoden kommer att få låset för det objektet och släppa det när dess operation är klar. I exemplet ovan kan vi göra vår "run ()" -metod som synkroniserad med hjälp av det "synkroniserade" nyckelordet efter åtkomstmodifieraren.

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

Utgången för detta fall kommer att vara:

Den aktuella tråden som körs tråd 1 Aktuell trådvärde 1

Den aktuella tråden som körs tråd 3 Aktuell trådvärde 2

Den aktuella tråden som körs tråd 2 Aktuell trådvärdet 3

ii. Statisk synkroniserad metod: För att synkronisera statiska metoder måste man skaffa sitt klassnivålås. När en tråd bara har fått klassnivålåset kan den utföra en statisk metod. Medan en tråd håller klassnivålåset, kan ingen annan tråd utföra någon annan statisk synkroniserad metod för den klassen. Men de andra trådarna kan köra någon annan vanlig metod eller vanlig statisk metod eller till och med icke-statisk synkroniserad metod i den klassen.

Låt oss till exempel överväga vår klass "Modify" och göra ändringar i den genom att konvertera vår "inkrement" -metod till en statisk synkroniserad metod. Kodändringarna är som nedan:

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. Synkroniserat block: En av de största nackdelarna med den synkroniserade metoden är att det ökar trådens väntetid som påverkar kodens prestanda. För att bara kunna synkronisera de erforderliga kodraderna istället för hela metoden måste man använda ett synkroniserat block. Användning av synkroniserat block minskar trådarnas väntetid och förbättrar också prestanda. I föregående exempel har vi redan använt synkroniserat block medan vi synkroniserade vår kod för första gången.

Exempel:
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ådkoordination:

För synkroniserade trådar är kommunikation mellan trådar en viktig uppgift. Inbyggda metoder som hjälper till att uppnå kommunikation mellan trådar för synkroniserad kod är nämligen:

  • vänta()
  • anmäla ()
  • notifyAll ()

Obs: Dessa metoder tillhör objektklassen och inte trådklassen. För att en tråd ska kunna påkalla dessa metoder på ett objekt bör den hålla fast låset på det objektet. Dessutom orsakar dessa metoder en tråd att frigöra sitt lås på föremålet som den åberopas på.

vänta (): En tråd på anropande wait () -metod, släpper låset på objektet och går i väntetillstånd. Den har två metodöverbelastningar:

  • public final void wait () kastar InterruptException
  • public final void wait (lång timeout) kastar InterruptException
  • public final void wait (lång timeout, int nanos) kastar InterruptException

meddela (): En tråd skickar en signal till en annan tråd i väntetillstånd genom att använda metoden meddelandet (). Den skickar meddelandet till endast en tråd så att den här tråden kan återuppta sin körning. Vilken tråd som får meddelandet bland alla trådar i väntetillstånd beror på Java Virtual Machine.

  • offentligt slutgiltigt meddelande ()

notifyAll (): När en tråd åberopar notifyAll () -metoden meddelas varje tråd i sitt väntetillstånd. Dessa trådar kommer att köras efter varandra baserat på den beställning som beslutas av Java Virtual Machine.

  • public final void notifyAll ()

Slutsats

I den här artikeln har vi sett hur arbetet i en multigängad miljö kan leda till datakonsekvens på grund av ett rasförhållande. Hur synkronisering hjälper oss att övervinna detta genom att begränsa en enda tråd att arbeta på en delad resurs åt gången. Också hur synkroniserade trådar kommunicerar med varandra.

Rekommenderade artiklar:

Detta har varit en guide till Vad är synkronisering i Java ?. Här diskuterar vi introduktion, förståelse, behov, arbete och typer av synkronisering med någon provkod. Du kan också gå igenom våra andra föreslagna artiklar för att lära dig mer -

  1. Serialisering i Java
  2. Vad är Generics i Java?
  3. Vad är API i Java?
  4. Vad är ett binärt träd i Java?
  5. Exempel och hur generiker fungerar i C #

Kategori: