JavaRush /Blog Java /Random-ES /¿Cuál es la diferencia entre un mutex, un monitor y un se...

¿Cuál es la diferencia entre un mutex, un monitor y un semáforo?

Publicado en el grupo Random-ES
¡Hola! Mientras estudiaba subprocesos múltiples en JavaRush, a menudo se encontraba con los conceptos de "mutex" y "monitor". ¿Puedes ahora, sin mirar, responder en qué se diferencian? :) ¿Cuál es la diferencia entre un mutex, un monitor y un semáforo? - 1Si pudieras, ¡bien hecho! Si no es así (y esto sucede con mayor frecuencia), no es de extrañar. De hecho, los conceptos de "mutex" y "monitor" están relacionados. Además, mientras lee conferencias y mira videos sobre subprocesos múltiples en recursos externos en Internet, se encontrará con otro concepto similar: "semáforo". Su funcionalidad también es muy similar a la de un monitor y un mutex. Por lo tanto, comprendamos estos tres términos, veamos algunos ejemplos y finalmente organicemos en nuestras cabezas la comprensión de en qué se diferencian entre sí :)

exclusión mutua

Un mutex es un objeto especial para sincronizar subprocesos. Está "adjunto" a cada objeto en Java; eso ya lo sabes :) No importa si usas clases estándar o creas tus propias clases, digamos, Caty Dog: todos los objetos de todas las clases tienen un mutex . El nombre "mutex" proviene del inglés "MUTual EXclusion" - "exclusión mutua", y esto refleja perfectamente su propósito. Como dijimos en una de las conferencias anteriores, la tarea de un mutex es proporcionar tal mecanismo para que solo un hilo tenga acceso a un objeto en un momento determinado . Una analogía popular para un mutex en la vida real es el "ejemplo del baño". Cuando una persona entra al baño, cierra la puerta desde adentro. El inodoro actúa como un objeto al que se puede acceder mediante múltiples hilos. La cerradura de la puerta del baño es el papel de un mutex, y la cola de personas afuera es el papel de los hilos. La cerradura de la puerta es un mutex sanitario: garantiza que sólo pueda estar dentro una persona a la vez. ¿Cuál es la diferencia entre un mutex, un monitor y un semáforo? - 2En otras palabras, sólo un hilo a la vez puede funcionar en recursos compartidos. Los intentos de otros subprocesos (personas) de acceder a los recursos ocupados fracasarán. Un mutex tiene varias características importantes. En primer lugar , sólo son posibles dos estados: "libre" y "ocupado". Esto hace que sea más fácil entender cómo funciona: se pueden trazar paralelos con variables booleanas verdadero/falso o el sistema numérico binario 1/0. En segundo lugar , los Estados no pueden controlarse directamente. No existen mecanismos en Java que le permitan tomar explícitamente un objeto, obtener su exclusión mutua y asignarle el estado deseado. En otras palabras, no puedes hacer algo como:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Por tanto, el mutex del objeto no se puede liberar. Sólo la máquina Java tiene acceso directo a él. Los programadores trabajan con mutex utilizando herramientas de lenguaje.

Monitor

Un monitor es un “complemento” adicional a un mutex. De hecho, el monitor es un fragmento de código “invisible” para el programador . Hablando anteriormente sobre el mutex, dimos un ejemplo simple:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //... algo de lógica disponible para todos los subprocesos

       synchronized (obj) {

           //lógica que solo está disponible para un subproceso a la vez
       }
   }
}
En el bloque de código que está marcado con la palabra synchronized, se captura el mutex de nuestro objeto obj. Bien, la captura ocurre, pero ¿cómo se logra exactamente el “mecanismo de defensa”? synchronized¿Por qué otros hilos no pueden entrar en el bloque cuando ven una palabra ? ¡Es el monitor el que crea el mecanismo de protección! El compilador convierte la palabra synchronizeden varios fragmentos de código especiales. Una vez más volvamos a nuestro ejemplo con el método doSomething()y agreguemosle:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //... algo de lógica disponible para todos los subprocesos

       //lógica que solo está disponible para un subproceso a la vez
       synchronized (obj) {

           /*выполнить важную работу, при которой доступ к un objetoу
           должен быть только у одного потока*/
           obj.someImportantMethod();
       }
   }
}
Esto es lo que sucederá "bajo el capó" de nuestro programa después de que el compilador convierta este código:
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       //... algo de lógica disponible para todos los subprocesos

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

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

       //пометить мьютекс un objetoа Cómo занятый
       obj.getMutex().isBusy() = true;

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

       //освободить мьютекс un objetoа
       obj.getMutex().isBusy() = false;
   }
}
El ejemplo, por supuesto, no es real. Aquí, utilizando código similar a Java, intentamos reflejar lo que está sucediendo en este momento dentro de la máquina Java. Sin embargo, este pseudocódigo brinda una gran comprensión de lo que realmente sucede con el objeto y los subprocesos dentro del bloque synchronizedy cómo el compilador convierte esta palabra en varios comandos que son "invisibles" para el programador. Básicamente, monitor en Java se expresa usando la palabrasynchronized . Todo el código que apareció en lugar de la palabra synchronizeden el último ejemplo es el monitor.

Semáforo

Otra palabra con la que te encuentras cuando estudias multiproceso por tu cuenta es "semáforo". Averigüemos qué es y en qué se diferencia de un monitor y un mutex. Un semáforo es un medio para sincronizar el acceso a un recurso. Su peculiaridad es que utiliza un contador al crear un mecanismo de sincronización. El contador nos dice cuántos subprocesos pueden acceder simultáneamente a un recurso compartido. ¿Cuál es la diferencia entre un mutex, un monitor y un semáforo? - 3Los semáforos en Java están representados por la clase Semaphore. Al crear objetos de semáforo, podemos utilizar los siguientes constructores:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
Pasamos al constructor:
  • int permits— valor inicial y máximo del contador. Es decir, cuántos subprocesos pueden acceder simultáneamente a un recurso compartido;

  • boolean fair- establecer el orden en el que los hilos recibirán acceso. Si fair= true , se concede acceso a los subprocesos en espera en el orden en que lo solicitaron. Si es falso , el orden lo determinará el programador de subprocesos.

Un ejemplo clásico del uso de semáforos es el problema de los filósofos almorzando .
¿Cuál es la diferencia entre un mutex, un monitor y un semáforo? - 4
Simplificaremos un poco sus términos para una mejor comprensión. Imaginemos que tenemos 5 filósofos que necesitan almorzar. Al mismo tiempo, tenemos una mesa y no pueden estar en ella más de dos personas al mismo tiempo. Nuestra tarea es alimentar a todos los filósofos. Ninguno de los dos debería pasar hambre, ni “bloquearse” al intentar sentarse a la mesa (hay que evitar el estancamiento). Así será nuestra clase de filósofo:
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 ("Qué-то пошло не так!");
       }
   }
}
Y aquí está el código para ejecutar nuestro programa:
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();
   }
}
Creamos un semáforo con una cuenta de 2 para satisfacer la condición de que solo dos filósofos puedan comer al mismo tiempo. Es decir, solo dos subprocesos pueden funcionar simultáneamente, porque nuestra clase Philosopherse hereda de Thread! Los métodos acquire()y release()de la clase Semaphorecontrolan su contador de permisos. El método acquire()solicita permiso para acceder a un recurso desde el semáforo. Si el contador > 0, se concede el permiso y el contador se reduce en 1. El método release()"libera" el permiso previamente concedido y lo devuelve al contador (incrementando el contador de concesión del semáforo en 1). ¿Qué obtenemos cuando ejecutamos el programa? ¿Se ha solucionado el problema? ¿Lucharán nuestros filósofos mientras esperan su turno? :) Este es el resultado de la consola que recibimos: Sócrates se sienta a la mesa ¡Platón se sienta a la mesa que Sócrates ha comido! Se levanta de la mesa: ¡Platón ha comido! Se levanta de la mesa. Aristóteles se sienta a la mesa. ¡Pitágoras se sienta a la mesa que Aristóteles ha comido! ¡Sale de la mesa que ha comido Pitágoras! Deja la mesa y Thales se sienta a la mesa que Thales ha comido. Se levanta de la mesa ¡ Lo hemos conseguido! Y aunque Thales tuvo que cenar solo, creo que no está enojado con nosotros :) Es posible que hayas notado algunas similitudes entre un mutex y un semáforo. Por lo general tienen la misma finalidad: sincronizar el acceso a algún recurso. ¿Cuál es la diferencia entre un mutex, un monitor y un semáforo? - 5La única diferencia es que el mutex de un objeto sólo puede ser adquirido por un hilo a la vez, mientras que en el caso de un semáforo se utiliza un contador de hilos y varios de ellos pueden acceder al recurso a la vez. Y esto no es sólo una similitud coincidente :) De hecho, un mutex es un semáforo de un solo lugar . Es decir, es un semáforo cuyo contador está inicialmente configurado en 1. También se le llama "semáforo binario" porque su contador sólo puede tener 2 valores: 1 ("libre") y 0 ("ocupado"). ¡Eso es todo! Como puede ver, todo resultó no ser tan confuso :) Ahora, si desea estudiar el tema de subprocesos múltiples con más detalle en Internet, le resultará un poco más fácil navegar por los conceptos. ¡Nos vemos en las próximas lecciones!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION