JavaRush /Java Blog /Random-IT /Qual è la differenza tra un mutex, un monitor e un semafo...

Qual è la differenza tra un mutex, un monitor e un semaforo

Pubblicato nel gruppo Random-IT
Ciao! Studiando il multithreading in JavaRush, ti sei spesso imbattuto nei concetti di "mutex" e "monitor". Puoi ora, senza sbirciare, rispondere in cosa differiscono? :) Qual è la differenza tra un mutex, un monitor e un semaforo - 1Se potessi, ben fatto! In caso contrario (e molto spesso ciò accade), non c'è da stupirsi. I concetti di "mutex" e "monitor" sono infatti correlati. Inoltre, leggendo lezioni e guardando video sul multithreading su risorse esterne su Internet, ti imbatterai in un altro concetto simile: "semaforo". Anche la sua funzionalità è in gran parte simile a un monitor e un mutex. Cerchiamo quindi di comprendere questi tre termini, diamo un'occhiata ad alcuni esempi e infine organizziamo nella nostra testa la comprensione di come differiscono l'uno dall'altro :)

Mutex

Un mutex è un oggetto speciale per la sincronizzazione dei thread. È "allegato" a ogni oggetto in Java - lo sai già :) Non importa se usi classi standard o crei le tue classi, diciamo, Cate Dog: tutti gli oggetti di tutte le classi hanno un mutex . Il nome "mutex" deriva dall'inglese "MUTual EXclusion" - "mutua esclusione", e questo riflette perfettamente il suo scopo. Come abbiamo detto in una delle lezioni precedenti, il compito di un mutex è fornire un meccanismo tale che solo un thread alla volta abbia accesso a un oggetto . Un’analogia popolare per un mutex nella vita reale è “l’esempio della toilette”. Quando una persona entra nella toilette, chiude la porta dall'interno. La toilette funge da oggetto a cui è possibile accedere da più thread. La serratura della porta del bagno ha il ruolo di mutex e la coda di persone fuori è il ruolo di thread. La serratura della porta è un mutex della toilette: garantisce che all'interno possa entrare solo una persona alla volta. Qual è la differenza tra un mutex, un monitor e un semaforo - 2In altre parole, solo un thread alla volta può lavorare sulle risorse condivise. I tentativi da parte di altri thread (persone) di accedere alle risorse occupate falliranno. Un mutex ha diverse caratteristiche importanti. Innanzitutto sono possibili solo due stati: “libero” e “occupato”. Ciò rende più semplice capire come funziona: si possono tracciare paralleli con le variabili booleane vero/falso o con il sistema di numerazione binario 1/0. In secondo luogo , gli Stati non possono essere controllati direttamente. Non esistono meccanismi in Java che consentano di prendere esplicitamente un oggetto, ottenere il suo mutex e assegnargli lo stato desiderato. In altre parole, non puoi fare qualcosa del tipo:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Pertanto, il mutex dell'oggetto non può essere rilasciato. Solo la macchina Java ha accesso diretto ad esso. I programmatori lavorano con i mutex utilizzando strumenti linguistici.

Tenere sotto controllo

Un monitor è un "add-on" aggiuntivo per un mutex. Il monitor , infatti, è un pezzo di codice “invisibile” al programmatore . Parlando del mutex in precedenza, abbiamo fornito un semplice esempio:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
Nel blocco di codice contrassegnato dalla parola synchronizedviene catturato il mutex del nostro oggetto obj. Ok, la cattura avviene, ma come si realizza esattamente il “meccanismo di difesa”? synchronizedPerché gli altri thread non possono entrare nel blocco quando vedono una parola ? È il monitor che crea il meccanismo di protezione! Il compilatore converte la parola synchronizedin diversi pezzi di codice speciali. Ancora una volta torniamo al nostro esempio con il metodo doSomething()e aggiungiamo ad esso:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       //logic that is only available to one thread at a time
       synchronized (obj) {

           /*выполнить важную работу, при которой доступ к an objectу
           должен быть только у одного потока*/
           obj.someImportantMethod();
       }
   }
}
Ecco cosa accadrà "dietro il cofano" del nostro programma dopo che il compilatore avrà convertito questo codice:
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       //...some logic available to all threads

       //логика, которая одновременно доступна только для одного потока:

       /*до тех пор, пока мьютекс an object занят -
       любой другой поток (кроме того, который его захватил), спит*/
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       //пометить мьютекс an object How занятый
       obj.getMutex().isBusy() = true;

       /*выполнить важную работу, при которой доступ к an objectу
       должен быть только у одного потока*/
       obj.someImportantMethod();

       //освободить мьютекс an object
       obj.getMutex().isBusy() = false;
   }
}
L'esempio, ovviamente, non è reale. Qui, utilizzando un codice simile a Java, abbiamo cercato di riflettere ciò che sta accadendo in questo momento all'interno della macchina Java. Tuttavia, questo pseudocodice fornisce un'ottima comprensione di ciò che sta realmente accadendo con l'oggetto e i thread all'interno del blocco synchronizede di come il compilatore converte questa parola in diversi comandi che sono "invisibili" al programmatore. Essenzialmente, monitor in Java si esprime utilizzando la parolasynchronized . Tutto il codice che appare al posto della parola synchronizednell'ultimo esempio è monitor.

Semaforo

Un'altra parola che incontri quando studi il multithreading da solo è "semaforo". Scopriamo cos'è e in cosa differisce da un monitor e da un mutex. Un semaforo è un mezzo per sincronizzare l'accesso a una risorsa. La sua particolarità è che utilizza un contatore durante la creazione di un meccanismo di sincronizzazione. Il contatore ci dice quanti thread possono accedere contemporaneamente a una risorsa condivisa. Qual è la differenza tra un mutex, un monitor e un semaforo - 3I semafori in Java sono rappresentati dalla classe Semaphore. Quando creiamo oggetti semaforo, possiamo utilizzare i seguenti costruttori:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
Passiamo al costruttore:
  • int permits— valore iniziale e massimo del contatore. Cioè quanti thread possono accedere contemporaneamente a una risorsa condivisa;

  • boolean fair- per stabilire l'ordine in cui i thread riceveranno l'accesso. Se fair= true , l'accesso viene concesso ai thread in attesa nell'ordine in cui lo hanno richiesto. Se è false , l'ordine sarà determinato dallo scheduler dei thread.

Un classico esempio dell'uso dei semafori è il problema dei filosofi a pranzo .
Qual è la differenza tra un mutex, un monitor e un semaforo - 4
Semplificheremo un po' i suoi termini per una migliore comprensione. Immagina di avere 5 filosofi che hanno bisogno di pranzo. Allo stesso tempo, abbiamo un tavolo e non possono esserci più di due persone contemporaneamente. Il nostro compito è nutrire tutti i filosofi. Nessuno dei due dovrebbe soffrire la fame, né “bloccarsi” a vicenda nel tentativo di sedersi a tavola (dobbiamo evitare situazioni di stallo). Ecco come apparirà la nostra classe di filosofo:
class Philosopher extends Thread {

   private Semaphore sem;

   // поел ли философ
   private boolean full = false;

   private String name;

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

   public void run()
   {
       try
       {
           // если философ еще не ел
           if (!full) {
               //Запрашиваем у семафора разрешение на выполнение
               sem.acquire();
               System.out.println (name + " садится за стол");

               // философ ест
               sleep(300);
               full = true;

               System.out.println (name + " поел! Он выходит из-за стола");
               sem.release();

               // философ ушел, освободив место другим
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println ("What-то пошло не так!");
       }
   }
}
Ed ecco il codice per eseguire il nostro programma:
public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem,"Сократ").start();
       new Philosopher(sem,"Платон").start();
       new Philosopher(sem,"Аристотель").start();
       new Philosopher(sem,"Фалес").start();
       new Philosopher(sem,"Пифагор").start();
   }
}
Abbiamo creato un semaforo con un conteggio pari a 2 per soddisfare la condizione che solo due filosofi possano mangiare contemporaneamente. Cioè, solo due thread possono funzionare contemporaneamente, perché la nostra classe Philosopherè ereditata da Thread! La classe acquire()e i metodi controllano il contatore dei permessi. Il metodo richiede l'autorizzazione per accedere a una risorsa dal semaforo. Se contatore > 0, l'autorizzazione viene concessa e il contatore viene decrementato di 1. Il metodo "rilascia" l'autorizzazione precedentemente concessa e la restituisce al contatore (incrementando di 1 il contatore di concessione del semaforo). Cosa otteniamo quando eseguiamo il programma? Il problema è stato risolto? I nostri filosofi combatteranno aspettando il loro turno? :) Questo è l'output della console che abbiamo ricevuto: Socrate si siede a tavola Platone si siede a tavola Socrate ha mangiato! Si alza da tavola: Platone ha mangiato! Si alza da tavola Aristotele si siede a tavola Pitagora si siede a tavola Aristotele ha mangiato! Si alza da tavola Pitagora ha mangiato! Si alza da tavola Talete si siede al tavolo che Talete ha mangiato! Si alza dal tavolo Ci siamo riusciti! E anche se Talete ha dovuto cenare da solo, penso che non sia arrabbiato con noi :) Potresti aver notato alcune somiglianze tra un mutex e un semaforo. In generale, hanno lo stesso scopo: sincronizzare l'accesso ad alcune risorse. L’unica differenza è che il mutex di un oggetto può essere acquisito solo da un thread alla volta, mentre nel caso di un semaforo viene utilizzato un contatore di thread e più di essi possono accedere alla risorsa contemporaneamente. E questa non è solo una somiglianza casuale :) In effetti, un mutex è un semaforo a posto singolo . Cioè, è un semaforo il cui contatore è inizialmente impostato su 1. Viene anche chiamato "semaforo binario" perché il suo contatore può avere solo 2 valori: 1 ("libero") e 0 ("occupato"). È tutto! Come puoi vedere, tutto si è rivelato non così confuso :) Ora, se vuoi studiare l'argomento del multithreading in modo più dettagliato su Internet, sarà un po 'più facile per te navigare tra i concetti. Ci vediamo alle prossime lezioni! release()Semaphoreacquire()release()Qual è la differenza tra un mutex, un monitor e un semaforo - 5
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION