CodeGym /Blog Java /Random-PL /Różnica między muteksem, monitorem i semaforem
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Różnica między muteksem, monitorem i semaforem

Opublikowano w grupie Random-PL
Cześć! Kiedy studiowałeś wielowątkowość w CodeGym, często spotykałeś się z pojęciami „mutex” i „monitor”. Bez podglądania możesz powiedzieć czym się różnią? :) Jeśli tak, dobra robota! Jeśli nie (jest to najczęstsze), nie jest to niespodzianką. „Mutex” i „monitor” to w rzeczywistości powiązane pojęcia. Dodatkowo, czytając lekcje i oglądając filmy o wielowątkowości na innych stronach internetowych, natkniesz się na inne podobne pojęcie: „semafor”. Ma również bardzo podobną funkcję do monitorów i muteksów. Dlatego zbadamy te trzy terminy. Przyjrzymy się kilku przykładom i ostatecznie zrozumiemy, czym te koncepcje różnią się od siebie :)

Muteks

Mutex (lub blokada) to specjalny mechanizm synchronizacji wątków. Jeden jest „dołączony” do każdego obiektu w Javie — to już wiesz :) Nie ma znaczenia, czy używasz standardowych klas, czy tworzysz własne, np. Kot i Pies : wszystkie obiekty wszystkich klas mają muteks . Termin „mutex” pochodzi od „MUTual EXclusion”, co doskonale opisuje jego przeznaczenie. Jak powiedzieliśmy w jednej z naszych poprzednich lekcji, muteks umożliwia zapewnienie, że tylko jeden wątek naraz ma dostęp do obiektu. Popularny przykład muteksu z życia wzięty z życia dotyczy toalet. Kiedy osoba wchodzi do przegrody toalety, zamyka drzwi od wewnątrz. Toaleta jest jak obiekt, do którego można uzyskać dostęp za pomocą wielu wątków. Zamek na drzwiach działowych jest jak muteks, a kolejka ludzi na zewnątrz reprezentuje wątki. Zamek w drzwiach to mutex toalety: zapewnia, że ​​tylko jedna osoba może wejść do środka. Jaka jest różnica między muteksem, monitorem i semaforem?  - 2Innymi słowy, tylko jeden wątek na raz może pracować ze współdzielonymi zasobami. Podejmowane przez inne wątki (ludzie) próby uzyskania dostępu do zajętych zasobów zakończą się niepowodzeniem. Mutex ma kilka ważnych cech. Po pierwsze , możliwe są tylko dwa stany: „odblokowany” i „zablokowany”. To pomaga nam zrozumieć, jak to działa: możesz rysować podobieństwa ze zmiennymi boolowskimi (prawda/fałsz) lub liczbami binarnymi (0/1). , stanem nie można sterować bezpośrednio. Java nie ma mechanizmu, który pozwoliłby jawnie wziąć obiekt, pobrać jego muteks i przypisać żądany status. Innymi słowy, nie możesz zrobić czegoś takiego:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Oznacza to, że nie można zwolnić muteksu obiektu. Tylko maszyna Java ma do niego bezpośredni dostęp. Programiści pracują z muteksami za pomocą narzędzi języka.

Monitor

Monitor to dodatkowa „nadbudowa” nad muteksem. W rzeczywistości monitor to fragment kodu, który jest „niewidoczny” dla programisty. Kiedy wcześniej mówiliśmy o muteksach, podaliśmy prosty przykład:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
W bloku kodu oznaczonym słowem kluczowym synchronized pobierany jest muteks naszego obiektu obj . Świetnie, możemy zdobyć zamek, ale jak dokładnie jest zapewniona „ochrona”? Kiedy widzimy słowo synchronized , co uniemożliwia innym wątkom wejście do bloku? Ochrona pochodzi z monitora! Kompilator konwertuje słowo kluczowe synchronized na kilka specjalnych fragmentów kodu. Jeszcze raz wróćmy do naszego przykładu z metodą doSomething() . Dodamy do tego:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time
       synchronized (obj) {

           /* Do important work that requires that the object
           be accessed by only one thread */
           obj.someImportantMethod();
       }
   }
}
Oto, co dzieje się „pod maską” po konwersji tego kodu przez kompilator:

public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time:
     
       /* as long as the object's mutex is busy,
       all the other threads (except the one that acquired it) are put to sleep */
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       // Mark the object's mutex as busy
       obj.getMutex().isBusy() = true;

       /* Do important work that requires that the object
       be accessed by only one thread */
       obj.someImportantMethod();

       // Free the object's mutex
       obj.getMutex().isBusy() = false;
   }
}
Oczywiście to nie jest prawdziwy przykład. Tutaj użyliśmy kodu podobnego do języka Java, aby zobrazować, co dzieje się wewnątrz maszyny Java. To powiedziawszy, ten pseudokod daje doskonałe zrozumienie tego, co faktycznie dzieje się z obiektem i wątkami wewnątrz zsynchronizowanego bloku oraz w jaki sposób kompilator konwertuje to słowo kluczowe na kilka instrukcji, które są „niewidoczne” dla programisty. Zasadniczo Java używa słowa kluczowego synchronized do reprezentowania monitora . Cały kod, który pojawia się zamiast słowa kluczowego synchronized w ostatnim przykładzie, to monitor.

Semafor

Innym słowem, które napotkasz podczas osobistego studiowania wielowątkowości, jest „semafor”. Zastanówmy się, co to jest i czym różni się od monitora i muteksu. Semafor to narzędzie do synchronizacji dostępu do jakiegoś zasobu. Jego charakterystyczną cechą jest to, że wykorzystuje licznik do stworzenia mechanizmu synchronizacji. Licznik mówi nam, ile wątków może jednocześnie uzyskać dostęp do udostępnionego zasobu. Jaka jest różnica między muteksem, monitorem i semaforem?  - 3Semafory w Javie są reprezentowane przez klasę Semaphore . Podczas tworzenia obiektów semaforowych możemy skorzystać z następujących konstruktorów:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
Do konstruktora przekazujemy:
    int dopuszcza — początkową i maksymalną wartość licznika. Innymi słowy, ten parametr określa, ile wątków może jednocześnie uzyskać dostęp do udostępnionego zasobu;
  • boolean fair — ustala kolejność, w jakiej wątki będą uzyskiwać dostęp. Jeśli fair jest prawdą, dostęp jest przyznawany wątkom oczekującym w kolejności, w jakiej o to prosiły. Jeśli jest fałszywa, kolejność jest określana przez program planujący wątki.
Klasycznym przykładem użycia semaforów jest problem filozofa jadalni. Jaka jest różnica między muteksem, monitorem i semaforem?  - 4Aby ułatwić zrozumienie, trochę to uprościmy. Wyobraź sobie, że mamy 5 filozofów, którzy muszą zjeść obiad. Dodatkowo dysponujemy jednym stołem, przy którym mogą jednocześnie pomieścić się nie więcej niż dwie osoby. Naszym zadaniem jest nakarmić wszystkich filozofów. Żadna z nich nie powinna chodzić głodna, ani też żadna z nich nie powinna się „blokować” przy próbie siadania do stołu (musimy unikać impasu). Oto jak będzie wyglądać nasza klasa filozofów:

class Philosopher extends Thread {

   private Semaphore sem;

   // Did the philosopher eat?
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // If the philosopher has not eaten
           if (!full) {
               // Ask the semaphore for permission to run
               sem.acquire();
               System.out.println(name + " takes a seat at the table");

               // The philosopher eats
               sleep(300);
               full = true;

               System.out.println(name + " has eaten! He leaves the table");
               sem.release();

               // The philosopher leaves, making room for others
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println("Something went wrong!");
       }
   }
}
A oto kod do uruchomienia naszego programu:

public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
Stworzyliśmy semafor, którego licznik jest ustawiony na 2, aby spełnić warunek: tylko dwóch filozofów może jeść w tym samym czasie. Oznacza to, że jednocześnie mogą działać tylko dwa wątki, ponieważ nasza klasa Philosopher dziedziczy Thread ! Metody nabyć () i release() klasy Semaphore sterują jej licznikiem dostępu. Metoda nabyć() prosi semafor o dostęp do zasobu. Jeśli licznik jest >0, dostęp jest udzielany, a licznik jest zmniejszany o 1. Funkcja release()metoda "zwalnia" przyznany wcześniej dostęp, zwracając go do licznika (zwiększa licznik dostępu semafora o 1). Co otrzymujemy uruchamiając program? Czy problem został rozwiązany? Czy nasi filozofowie nie będą walczyć, czekając na swoją kolej? :) Oto wynik konsoli, który otrzymaliśmy:

Socrates takes a seat at the table 
Plato takes a seat at the table 
Socrates has eaten! He leaves the table 
Plato has eaten! He leaves the table 
Aristotle takes a seat at the table 
Pythagoras takes a seat at the table 
Aristotle has eaten! He leaves the table 
Pythagoras has eaten! He leaves the table 
Thales takes a seat at the table 
Thales has eaten! He leaves the table 
Zrobiliśmy to! I chociaż Thales musiał zjeść obiad sam, myślę, że go nie uraziliśmy :) Być może zauważyłeś pewne podobieństwa między muteksem a semaforem. Rzeczywiście, mają tę samą misję: zsynchronizować dostęp do jakiegoś zasobu. Jaka jest różnica między muteksem, monitorem i semaforem?  - 5Jedyna różnica polega na tym, że muteks obiektu może być pobrany tylko przez jeden wątek na raz, podczas gdy w przypadku semafora, który używa licznika wątków, kilka wątków może uzyskać dostęp do zasobu jednocześnie. To nie jest zwykły zbieg okoliczności :) Muteks to tak naprawdę semaforz liczbą 1. Innymi słowy, jest to semafor, który może obsłużyć pojedynczy wątek. Jest również znany jako „semafor binarny”, ponieważ jego licznik może mieć tylko 2 wartości — 1 („odblokowany”) i 0 („zablokowany”). Otóż ​​to! Jak widzisz, wcale nie jest to takie zagmatwane :) Teraz, jeśli chcesz bardziej szczegółowo studiować wielowątkowość w Internecie, będzie ci trochę łatwiej poruszać się po tych pojęciach. Do zobaczenia na kolejnych lekcjach!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION