CodeGym /Blog Java /Random-PL /Synchronizacja wątków. Operator synchroniczny
Autor
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Synchronizacja wątków. Operator synchroniczny

Opublikowano w grupie Random-PL
Cześć! Dzisiaj będziemy nadal rozważać funkcje programowania wielowątkowego i mówić o synchronizacji wątków. Synchronizacja wątków.  Operator synchroniczny - 1

Co to jest synchronizacja w Javie?

Poza domeną programowania oznacza układ, który umożliwia współpracę dwóch urządzeń lub programów. Na przykład smartfon i komputer można zsynchronizować z kontem Google, a konto w witrynie internetowej z kontami w sieciach społecznościowych, dzięki czemu można się na nich logować. Synchronizacja wątków ma podobne znaczenie: jest to układ, w którym wątki wchodzą w interakcję z nawzajem. Na poprzednich lekcjach nasze wątki żyły i działały oddzielnie od siebie. Jeden wykonywał obliczenia, drugi spał, a trzeci wyświetlał coś na konsoli, ale nie wchodzili w interakcje. W prawdziwych programach takie sytuacje zdarzają się rzadko. Wiele wątków może aktywnie pracować z tym samym zestawem danych i modyfikować go. To stwarza problemy. Wyobraź sobie wiele wątków zapisujących tekst w tym samym miejscu, na przykład do pliku tekstowego lub konsoli. W takim przypadku plik lub konsola stają się zasobem udostępnionym. Wątki nie są świadome swojego istnienia, więc po prostu piszą wszystko, co mogą w czasie przydzielonym im przez program planujący wątki. Podczas ostatniej lekcji widzieliśmy przykład, dokąd to prowadzi. Przypomnijmy to sobie teraz: Synchronizacja wątków.  Operator synchroniczny - 2Przyczyna leży w tym, że wątki pracują ze współdzielonym zasobem (konsolą) bez koordynowania swoich działań. Jeśli program planujący wątki przydzieli czas Wątkowi-1, natychmiast zapisuje wszystko w konsoli. To, co inne wątki mają lub nie zdążyły już napisać, nie ma znaczenia. Rezultat, jak widać, jest przygnębiający. Dlatego wprowadzili do programowania wielowątkowego specjalną koncepcję, mutex (wzajemne wykluczanie) . Przeznaczenie muteksujest zapewnienie mechanizmu, dzięki któremu tylko jeden wątek ma dostęp do obiektu w określonym czasie. Jeśli wątek-1 uzyska muteks obiektu A, inne wątki nie będą mogły uzyskać dostępu do obiektu ani go modyfikować. Pozostałe wątki muszą czekać, aż muteks obiektu A zostanie zwolniony. Oto przykład z życia: wyobraź sobie, że ty i 10 innych nieznajomych uczestniczycie w ćwiczeniu. Na zmianę musisz wyrazić swoje pomysły i omówić coś. Ale ponieważ widzicie się po raz pierwszy, aby nie przeszkadzać sobie ciągle i nie wpadać w szał, używacie „mówiącej piłki”: tylko osoba z piłką może mówić. W ten sposób zakończysz dobrą i owocną dyskusję. Zasadniczo piłka jest muteksem. Jeśli mutex obiektu znajduje się w rękach jednego wątku, inne wątki nie mogą pracować z obiektem.Objectclass, co oznacza, że ​​każdy obiekt w Javie ma taką klasę.

Jak działa zsynchronizowany operator

Poznajmy nowe słowo kluczowe: synchronized . Służy do oznaczania określonego bloku kodu. Jeśli blok kodu jest oznaczony słowem synchronizedkluczowym, blok ten może być wykonywany tylko przez jeden wątek na raz. Synchronizację można przeprowadzić na różne sposoby. Na przykład deklarując synchronizację całej metody:

public synchronized void doSomething() {

   // ...Method logic
}
Lub napisz blok kodu, w którym synchronizacja jest wykonywana za pomocą jakiegoś obiektu:

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
       }
   }
}
Znaczenie jest proste. Jeśli jeden wątek wejdzie do bloku kodu oznaczonego słowem synchronizedkluczowym, natychmiast przechwytuje muteks obiektu, a wszystkie inne wątki próbujące wejść do tego samego bloku lub metody są zmuszone czekać, aż poprzedni wątek zakończy swoją pracę i zwolni monitor. Synchronizacja wątków.  Operator synchroniczny - 3Przy okazji! Podczas kursu widzieliście już przykłady synchronized, ale wyglądały one inaczej:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Temat jest dla Ciebie nowy. I oczywiście będzie zamieszanie ze składnią. Więc zapamiętaj to od razu, aby później uniknąć zdezorientowania różnymi sposobami pisania. Te dwa sposoby zapisu oznaczają to samo:

public void swap() {

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


public synchronized void swap() {

   }
}
W pierwszym przypadku tworzysz zsynchronizowany blok kodu natychmiast po wprowadzeniu metody. Jest synchronizowany przez thisobiekt, czyli bieżący obiekt. W drugim przykładzie stosujesz słowo synchronizedkluczowe do całej metody. Dzięki temu nie jest konieczne jawne wskazanie obiektu używanego do synchronizacji. Ponieważ cała metoda jest oznaczona słowem kluczowym, metoda zostanie automatycznie zsynchronizowana dla wszystkich instancji klasy. Nie będziemy zagłębiać się w dyskusję o tym, który sposób jest lepszy. Na razie wybierz sposób, który najbardziej Ci odpowiada :) Najważniejszą rzeczą jest zapamiętanie: możesz zadeklarować metodę zsynchronizowaną tylko wtedy, gdy cała jej logika jest wykonywana przez jeden wątek na raz. Na przykład błędem byłoby zsynchronizowanie następującej doSomething()metody:

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
       }
   }
}
Jak widać, część metody zawiera logikę, która nie wymaga synchronizacji. Ten kod może być uruchamiany przez wiele wątków jednocześnie, a wszystkie krytyczne miejsca są oddzielone w osobnym synchronizedbloku. I jeszcze jedno. Przyjrzyjmy się bliżej naszemu przykładowi z lekcji z zamianą nazw:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Uwaga: synchronizacja odbywa się za pomocąthis. To znaczy przy użyciu określonegoMyClassobiektu. Załóżmy, że mamy 2 wątki (Thread-1iThread-2) i tylko jedenMyClass myClassobiekt. W takim przypadku, jeśliThread-1wywołamyClass.swap()metodę, muteks obiektu będzie zajęty, a podczas próby wywołania metodymyClass.swap()zawiesiThread-2się podczas oczekiwania na zwolnienie muteksu. Jeśli będziemy mieli 2 wątki i 2MyClassobiekty (myClass1imyClass2), nasze wątki mogą z łatwością jednocześnie wykonywać zsynchronizowane metody na różnych obiektach. Pierwszy wątek wykonuje to:

myClass1.swap();
Drugi wykonuje to:

myClass2.swap();
W tym przypadku synchronizedsłowo kluczowe wewnątrz swap()metody nie wpłynie na działanie programu, ponieważ synchronizacja jest wykonywana przy użyciu określonego obiektu. A w tym drugim przypadku mamy 2 obiekty. Dzięki temu wątki nie stwarzają sobie nawzajem problemów. W końcu dwa obiekty mają 2 różne muteksy, a zdobycie jednego jest niezależne od zdobycia drugiego .

Cechy szczególne synchronizacji w metodach statycznych

Ale co, jeśli musisz zsynchronizować metodę statyczną ?

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

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

}
Nie jest jasne, jaką rolę odegra tutaj muteks. W końcu ustaliliśmy już, że każdy obiekt ma muteks. Ale problem polega na tym, że nie potrzebujemy obiektów do wywołania metody MyClass.swap(): metoda jest statyczna! Więc, co dalej? :/ Właściwie nie ma tu problemu. Twórcy Javy zadbali o wszystko :) Jeśli metoda zawierająca krytyczną logikę współbieżną jest statyczna, to synchronizacja odbywa się na poziomie klasy. Dla większej przejrzystości możemy przepisać powyższy kod w następujący sposób:

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;
       }
   }

}
W zasadzie sam mógłbyś o tym pomyśleć: ponieważ nie ma obiektów, mechanizm synchronizacji musi być w jakiś sposób wbudowany w samą klasę. I tak właśnie jest: możemy użyć klas do synchronizacji.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION