CodeGym /Java Blog /Random-IT /Differenza tra un mutex, un monitor e un semaforo
John Squirrels
Livello 41
San Francisco

Differenza tra un mutex, un monitor e un semaforo

Pubblicato nel gruppo Random-IT
CIAO! Quando hai studiato il multithreading su CodeGym, hai incontrato spesso i concetti di "mutex" e "monitor". Senza sbirciare, puoi dire in cosa differiscono? :) Se sì, ben fatto! In caso contrario (questo è il più comune), non è una sorpresa. "Mutex" e "monitor" sono in realtà concetti correlati. Inoltre, quando leggi lezioni e guardi video sul multithreading su altri siti web, ti imbatterai in un altro concetto simile: "semaforo". Ha anche una funzione molto simile ai monitor e ai mutex. Ecco perché esamineremo questi tre termini. Vedremo alcuni esempi e arriveremo a una comprensione definitiva di come questi concetti differiscono l'uno dall'altro :)

Mutex

Un mutex (o blocco) è un meccanismo speciale per sincronizzare i thread. Uno è "allegato" a ogni oggetto in Java - lo sai già :) Non importa se usi classi standard o crei le tue classi, ad esempio Cat and Dog : tutti gli oggetti di tutte le classi hanno un mutex . Il termine "mutex" deriva da "MUTual EXclusion", che descrive perfettamente il suo scopo. Come abbiamo detto in una delle nostre lezioni precedenti, un mutex permette di garantire che solo un thread alla volta abbia accesso all'oggetto. Un popolare esempio di mutex nella vita reale riguarda i servizi igienici. Quando una persona entra in una partizione del bagno, chiude la porta dall'interno. La toilette è come un oggetto a cui è possibile accedere da più thread. Il lucchetto sulla porta della partizione è come un mutex e la fila di persone all'esterno rappresenta i fili. La serratura della porta è il mutex della toilette: fa sì che possa entrare solo una persona. Qual è la differenza tra un mutex, un monitor e un semaforo?  - 2In altre parole, solo un thread alla volta può lavorare con risorse condivise. I tentativi da parte di altri thread (persone) di ottenere l'accesso alle risorse occupate falliranno. Un mutex ha diverse caratteristiche importanti. Primo , sono possibili solo due stati: "sbloccato" e "bloccato". Questo ci aiuta a capire come funziona: puoi tracciare parallelismi con variabili booleane (vero/falso) o numeri binari (0/1). , lo stato non può essere controllato direttamente. Java non ha alcun meccanismo che ti consenta di prendere esplicitamente un oggetto, ottenere il suo mutex e assegnare lo stato desiderato. In altre parole, non puoi fare qualcosa del tipo:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Ciò significa che non puoi rilasciare il mutex di un oggetto. Solo la macchina Java ha accesso diretto ad esso. I programmatori lavorano con i mutex attraverso gli strumenti del linguaggio.

Tenere sotto controllo

Un monitor è una "sovrastruttura" aggiuntiva su un mutex. In effetti, un monitor è un blocco di codice "invisibile" al programmatore. Quando abbiamo parlato di mutex in precedenza, abbiamo fornito un semplice esempio:

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
       }
   }
}
Nel blocco di codice contrassegnato dalla parola chiavesynchronized viene acquisito il mutex del nostro oggetto obj . Ottimo, possiamo acquisire la serratura, ma come viene fornita esattamente la "protezione"? Quando vediamo la parola sincronizzato , cosa impedisce agli altri thread di entrare nel blocco? La protezione arriva da un monitor! Il compilatore converte la parola chiave sincronizzata in diverse parti speciali di codice. Ancora una volta, torniamo al nostro esempio con il metodo doSomething() . Aggiungeremo ad esso:

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();
       }
   }
}
Ecco cosa succede "sotto il cofano" dopo che il compilatore ha convertito questo codice:

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;
   }
}
Naturalmente, questo non è un vero esempio. Qui, abbiamo utilizzato codice simile a Java per rappresentare ciò che accade all'interno della macchina Java. Detto questo, questo pseudo-codice fornisce un'eccellente comprensione di ciò che accade effettivamente con l'oggetto e i thread all'interno del blocco sincronizzato e di come il compilatore converte questa parola chiave in diverse istruzioni che sono "invisibili" al programmatore. Fondamentalmente, Java utilizza la parola chiave sincronizzata per rappresentare un monitor . Tutto il codice che appare al posto della parola chiave sincronizzata nell'ultimo esempio è il monitor.

Semaforo

Un'altra parola che incontrerai nel tuo studio personale del multithreading è "semaforo". Scopriamo cos'è e in che modo differisce da un monitor e un mutex. Un semaforo è uno strumento per sincronizzare l'accesso ad alcune risorse. La sua caratteristica distintiva è che utilizza un contatore per creare il meccanismo di sincronizzazione. Il contatore ci dice quanti thread possono accedere contemporaneamente alla 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 usare i seguenti costruttori:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
Passiamo quanto segue al costruttore:
    int permessi — il valore iniziale e massimo del contatore. In altre parole, questo parametro determina quanti thread possono accedere contemporaneamente alla risorsa condivisa;
  • boolean fair — stabilisce l'ordine in cui i thread otterranno l'accesso. Se fair è vero, l'accesso viene concesso ai thread in attesa nell'ordine in cui lo hanno richiesto. Se è false, l'ordine è determinato dallo scheduler dei thread.
Un classico esempio di uso del semaforo è il problema del filosofo del pranzo. Qual è la differenza tra un mutex, un monitor e un semaforo?  - 4Per facilitare la comprensione, lo semplificheremo un po'. Immagina di avere 5 filosofi che hanno bisogno di pranzare. Inoltre, abbiamo un tavolo che può ospitare contemporaneamente non più di due persone. Il nostro compito è nutrire tutti i filosofi. Nessuno di loro dovrebbe soffrire la fame e nessuno di loro dovrebbe "bloccarsi" a vicenda quando cerca di sedersi al tavolo (dobbiamo evitare lo stallo). Ecco come sarà la nostra classe di filosofi:

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!");
       }
   }
}
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, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
Abbiamo creato un semaforo il cui contatore è posto a 2 per soddisfare la condizione: solo due filosofi possono mangiare contemporaneamente. Cioè, solo due thread possono essere eseguiti contemporaneamente, perché la nostra classe Philosopher eredita Thread ! I metodi acquire() e release() della classe Semaphore controllano il suo contatore di accesso. Il metodo acquire() chiede al semaforo l'accesso alla risorsa. Se il contatore è >0, viene concesso l'accesso e il contatore viene ridotto di 1. Il release()Il metodo "rilascia" l'accesso precedentemente concesso, restituendolo al contatore (aumenta di 1 il contatore di accesso del semaforo). Cosa otteniamo quando eseguiamo il programma? Il problema è risolto? I nostri filosofi non combatteranno mentre aspettano il loro turno? :) Ecco l'output della console che abbiamo ottenuto:

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 
Ce l'abbiamo fatta! E sebbene Thales abbia dovuto cenare da solo, non credo che lo abbiamo offeso :) Potresti aver notato alcune somiglianze tra un mutex e un semaforo. In effetti, hanno la stessa missione: sincronizzare l'accesso a qualche risorsa. Qual è la differenza tra un mutex, un monitor e un semaforo?  - 5L'unica differenza è che il mutex di un oggetto può essere acquisito da un solo thread alla volta, mentre nel caso di un semaforo, che utilizza un contatore di thread, più thread possono accedere alla risorsa contemporaneamente. Questa non è solo una coincidenza :) Un mutex è in realtà un semaforocon un conteggio pari a 1. In altre parole, è un semaforo che può ospitare un singolo thread. È anche noto come "semaforo binario" perché il suo contatore può avere solo 2 valori: 1 ("sbloccato") e 0 ("bloccato"). Questo è tutto! Come puoi vedere, dopotutto non è così confuso :) Ora, se vuoi studiare il multithreading in modo più dettagliato su Internet, sarà un po' più facile per te navigare in questi concetti. Ci vediamo alle prossime lezioni!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION