"¡Hola, amigo! Tenemos una panacea, una cura para todas las enfermedades. Como ya hemos visto, el cambio de hilo sin control es un problema".

"¿Por qué los subprocesos no pueden decidir cuándo cambiar al siguiente subproceso? ¿Hacen todo lo que necesitan hacer y luego señalan: "¡Terminé!"?"

"Permitir que los subprocesos controlen la conmutación por sí mismos sería un problema aún mayor. Supongamos que tiene un código mal escrito y el subproceso nunca entrega la CPU. En el pasado, así es como funcionaba. Y era toda una pesadilla".

"Está bien. Entonces, ¿cuál es la solución?"

" Bloqueo de otros hilos.  Así es como funciona".

Quedó claro que los subprocesos interfieren entre sí cuando intentan usar objetos y/o recursos compartidos . Tal como vimos en el ejemplo con la salida de la consola: hay una consola y todos los subprocesos se envían a ella. Está desordenado.

Así que se inventó un objeto especial: el mutex . Es como un cartel en la puerta de un baño que dice «disponible / ocupado» . Tiene dos estados: el objeto está disponible u ocupado . Estos estados también se denominan «bloqueado» y «desbloqueado».

Cuando un subproceso necesita un objeto compartido con otros subprocesos, verifica el mutex asociado con el objeto. Si el mutex está desbloqueado, el subproceso lo bloquea (lo marca como «ocupado») y comienza a usar el recurso compartido. Una vez que el subproceso ha hecho su trabajo, el mutex se desbloquea (marcado como «disponible»).

Si el subproceso quiere usar el objeto y el mutex está bloqueado, entonces el subproceso duerme mientras espera. Cuando el subproceso de ocupación finalmente desbloquee el mutex, nuestro subproceso lo bloqueará inmediatamente y comenzará a ejecutarse. La analogía con el letrero de la puerta del baño es perfecta.

"Entonces, ¿cómo trabajo con un mutex? ¿Necesito crear objetos especiales?"

"Es mucho más simple que eso. Los creadores de Java integraron este mutex en la clase Object. Así que ni siquiera tiene que crearlo. Es parte de cada objeto. Así es como funciona todo:"

Código Descripción
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
El método de intercambio intercambia los valores de las variables nombre1 y nombre2.

¿Qué podría pasar si se llama desde dos subprocesos al mismo tiempo?

Ejecución de código real Código del primer hilo Código del segundo hilo
String s1 = name1; //Ally
name1 = name2; //Lena
name2 = s1; //Ally

String s2 = name1; //Lena
name1 = name2; //Ally
name2 = s2; //Lena
String s1 = name1;
name1 = name2;
//other thread is running
name2 = s1;
//the thread waits until the mutex is unlocked

String s2 = name1;
name1 = name2;
//other thread is running
//other thread is running
name2 = s2;
La línea de fondo
Los valores de las variables se intercambiaron dos veces, volviendo a sus lugares originales.

Preste atención a la palabra clave  sincronizada .

"Sí, ¿qué significa?"

"Cuando un subproceso ingresa a un bloque de código marcado como sincronizado, la máquina Java bloquea inmediatamente el mutex del objeto indicado entre paréntesis después de la palabra sincronizado. Ningún otro subproceso puede ingresar a este bloque hasta que nuestro subproceso lo abandone. Tan pronto como nuestro subproceso lo deje el bloque marcado como sincronizado, el mutex se desbloquea de forma inmediata y automática y estará disponible para ser adquirido por otro subproceso".

Si el mutex está ocupado, nuestro subproceso se detendrá y esperará a que se libere.

"Tan simple y tan elegante. Esa es una hermosa solución".

"Sí. Pero, ¿qué crees que pasará en este caso?"

Código Descripción
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

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

public void swap2()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Los métodos swap e swap2 comparten el mismo mutex (el objeto this ).

¿Qué sucede si un subproceso llama al método swap y otro subproceso llama al método swap2?

"Dado que el mutex es el mismo, el segundo hilo tendrá que esperar hasta que el primer hilo abandone el bloque sincronizado. Así que no habrá ningún problema con el acceso simultáneo".

"¡Bien hecho, Amigo! ¡Esa es la respuesta correcta!"

Ahora me gustaría señalar que sincronizado se puede usar para marcar no solo bloques de código, sino también métodos. Esto es lo que eso significa:

Código lo que realmente sucede
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

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

public static synchronized void swap2()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

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

public static void swap2()
{
synchronized (MyClass.class)
{
String s = name1;
name1 = name2;
name2 = s;
}
}