CodeGym /Java Blog /Willekeurig /Draadsynchronisatie. De gesynchroniseerde operator
John Squirrels
Niveau 41
San Francisco

Draadsynchronisatie. De gesynchroniseerde operator

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we verder in op de kenmerken van multithreaded programmeren en praten we over threadsynchronisatie. Draadsynchronisatie.  De gesynchroniseerde operator - 1

Wat is synchronisatie in Java?

Buiten het programmeerdomein impliceert het een opstelling waarmee twee apparaten of programma's kunnen samenwerken. Een smartphone en computer kunnen bijvoorbeeld worden gesynchroniseerd met een Google-account, en een website-account kan worden gesynchroniseerd met sociale netwerkaccounts, zodat u ze kunt gebruiken om in te loggen. Threadsynchronisatie heeft een vergelijkbare betekenis: het is een opstelling waarin threads communiceren met elkaar. In voorgaande lessen leefden en werkten onze draden gescheiden van elkaar. Eén voerde een berekening uit, een tweede sliep en een derde liet iets op de console zien, maar ze communiceerden niet. In echte programma's zijn dergelijke situaties zeldzaam. Meerdere threads kunnen actief met dezelfde dataset werken en deze wijzigen. Dit zorgt voor problemen. Stel je voor dat meerdere threads tekst naar dezelfde plek schrijven, bijvoorbeeld naar een tekstbestand of de console. In dit geval wordt het bestand of de console een gedeelde bron. De threads zijn zich niet bewust van elkaars bestaan, dus schrijven ze gewoon alles wat ze kunnen in de tijd die hen is toegewezen door de threadplanner. In een recente les zagen we een voorbeeld van waar dit toe leidt. Laten we het nu onthouden: Draadsynchronisatie.  De gesynchroniseerde operator - 2De reden ligt in het feit dat de threads werken met een gedeelde bron (de console) zonder hun acties met elkaar te coördineren. Als de threadplanner tijd toewijst aan Thread-1, schrijft hij alles onmiddellijk naar de console. Wat andere threads wel of niet al hebben weten te schrijven, doet er niet toe. Het resultaat is, zoals u kunt zien, deprimerend. Daarom introduceerden ze een speciaal concept, de mutex (wederzijdse uitsluiting) , voor multithreaded programmeren. Het doel van een mutexis om een ​​mechanisme te bieden zodat slechts één thread op een bepaald moment toegang heeft tot een object. Als Thread-1 de mutex van object A verwerft, kunnen de andere threads het object niet openen en wijzigen. De andere threads moeten wachten tot de mutex van object A wordt vrijgegeven. Hier is een voorbeeld uit het leven: stel je voor dat jij en 10 andere vreemden meedoen aan een oefening. Om beurten moet u uw ideeën uiten en iets bespreken. Maar omdat je elkaar voor het eerst ziet, om elkaar niet constant te storen en in woede uit te barsten, gebruik je een 'talking ball': alleen de persoon met de bal kan spreken. Zo krijg je uiteindelijk een goed en vruchtbaar gesprek. In wezen is de bal een mutex. Als de mutex van een object in handen is van één thread, kunnen andere threads niet werken met het object.Objectklasse, wat betekent dat elk object in Java er een heeft.

Hoe de gesynchroniseerde operator werkt

Laten we een nieuw trefwoord leren kennen: gesynchroniseerd . Het wordt gebruikt om een ​​bepaald codeblok te markeren. Als een codeblok is gemarkeerd met het synchronizedtrefwoord, kan dat blok slechts door één thread tegelijk worden uitgevoerd. Synchronisatie kan op verschillende manieren worden geïmplementeerd. Bijvoorbeeld door te declareren dat een hele methode moet worden gesynchroniseerd:

public synchronized void doSomething() {

   // ...Method logic
}
Of schrijf een codeblok waarin synchronisatie wordt uitgevoerd met behulp van een object:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
De betekenis is simpel. Als een thread binnen het codeblok gaat dat is gemarkeerd met het synchronizedtrefwoord, legt het onmiddellijk de mutex van het object vast, en alle andere threads die hetzelfde blok of dezelfde methode proberen binnen te gaan, worden gedwongen te wachten tot de vorige thread zijn werk voltooit en de monitor vrijgeeft. Draadsynchronisatie.  De gesynchroniseerde operator - 3Trouwens! Tijdens de cursus heb je al voorbeelden gezien van synchronized, maar die zagen er anders uit:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Het onderwerp is nieuw voor je. En natuurlijk zal er verwarring zijn met de syntaxis. Onthoud het dus meteen om te voorkomen dat je later in de war raakt door de verschillende manieren om het te schrijven. Deze twee manieren om het te schrijven betekenen hetzelfde:

public void swap() {

   synchronized (this)
   {
       // ...Method logic
   }
}


public synchronized void swap() {

   }
}
In het eerste geval maak je direct na het invoeren van de methode een gesynchroniseerd codeblok aan. Het wordt gesynchroniseerd door het thisobject, dwz het huidige object. En in het tweede voorbeeld pas je het synchronizedtrefwoord toe op de hele methode. Dit maakt het onnodig om expliciet aan te geven welk object wordt gebruikt voor synchronisatie. Aangezien de hele methode is gemarkeerd met het trefwoord, wordt de methode automatisch gesynchroniseerd voor alle instanties van de klasse. We gaan niet in discussie over welke manier beter is. Kies voor nu de manier die je het beste bevalt :) Het belangrijkste is om te onthouden: je kunt een methode alleen gesynchroniseerd declareren als alle logica door één thread tegelijk wordt uitgevoerd. Het zou bijvoorbeeld een vergissing zijn om de volgende doSomething()methode te synchroniseren:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
Zoals u kunt zien, bevat een deel van de methode logica die geen synchronisatie vereist. Die code kan tegelijkertijd door meerdere threads worden uitgevoerd en alle kritieke plaatsen zijn apart gezet in een apart synchronizedblok. En nog een laatste ding. Laten we ons voorbeeld uit de les met naamswisseling eens nader bekijken:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Opmerking: de synchronisatie wordt uitgevoerd met behulp vanthis. Dat wil zeggen, een specifiekMyClassobject gebruiken. Stel dat we 2 threads (Thread-1enThread-2) en slechts éénMyClass myClassobject hebben. In dit geval, alsThread-1demyClass.swap()methode wordt aangeroepen, zal de mutex van het object bezet zijn, en wanneer een poging wordt gedaan om demyClass.swap()methodeThread-2, zal deze blijven hangen terwijl wordt gewacht tot de mutex wordt vrijgegeven. MyClassAls we 2 threads en 2 objecten (myClass1enhebbenmyClass2, kunnen onze threads gemakkelijk gelijktijdig de gesynchroniseerde methoden op verschillende objecten uitvoeren. De eerste thread voert dit uit:

myClass1.swap();
De tweede voert dit uit:

myClass2.swap();
In dit geval heeft het synchronizedsleutelwoord in de swap()methode geen invloed op de werking van het programma, aangezien de synchronisatie wordt uitgevoerd met behulp van een specifiek object. En in het laatste geval hebben we 2 objecten. De threads veroorzaken dus geen problemen voor elkaar. Twee objecten hebben immers 2 verschillende mutexen, en het verwerven van het ene staat los van het verwerven van het andere .

Speciale kenmerken van synchronisatie in statische methoden

Maar wat als u een statische methode moet synchroniseren ?

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

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

}
Het is niet duidelijk welke rol de mutex hier gaat spelen. We hebben immers al vastgesteld dat elk object een mutex heeft. Maar het probleem is dat we geen objecten nodig hebben om de MyClass.swap()methode aan te roepen: de methode is statisch! Dus wat nu? :/ Er is hier eigenlijk geen probleem. De makers van Java hebben voor alles gezorgd :) Als een methode die kritieke concurrente logica bevat statisch is, wordt de synchronisatie uitgevoerd op klasseniveau. Voor meer duidelijkheid kunnen we de bovenstaande code als volgt herschrijven:

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

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
In principe had je dit zelf kunnen bedenken: omdat er geen objecten zijn, moet het synchronisatiemechanisme op de een of andere manier in de klasse zelf worden ingebakken. En zo is het: we kunnen klassen gebruiken om te synchroniseren.
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION