"Ciao, Amigo! Abbiamo una panacea, una cura per tutte le malattie. Come abbiamo già visto, il cambio di thread incontrollato è un problema."

"Perché i thread stessi non possono decidere quando passare al thread successivo? Fai tutto quello che devono fare e poi segnala, «Ho finito!»?"

"Consentire ai thread stessi di controllare la commutazione sarebbe un problema ancora più grande. Supponiamo che tu abbia un codice scritto male e che il thread non si arrenda mai alla CPU. In passato, è così che funzionava. Ed è stato un vero incubo."

"Va bene. Allora qual è la soluzione?"

" Blocco di altri thread.  Ecco come funziona."

È diventato chiaro che i thread interferiscono tra loro quando tentano di utilizzare oggetti e/o risorse condivisi . Proprio come abbiamo visto nell'esempio con l'output della console: c'è una console e tutti i thread vengono inviati ad essa. È disordinato.

Così è stato inventato un oggetto speciale: il mutex . È come un cartello sulla porta del bagno che dice «disponibile / occupato» . Ha due stati: l'oggetto è disponibile o occupato . Questi stati sono anche chiamati «bloccati» e «sbloccati».

Quando un thread ha bisogno di un oggetto condiviso con altri thread, controlla il mutex associato all'oggetto. Se il mutex è sbloccato, il thread lo blocca (lo contrassegna come «occupato») e inizia a utilizzare la risorsa condivisa. Dopo che il thread ha svolto la sua attività, il mutex viene sbloccato (contrassegnato come «disponibile»).

Se il thread desidera utilizzare l'oggetto e il mutex è bloccato, il thread dorme mentre attende. Quando il mutex viene finalmente sbloccato dal thread che lo occupa, il nostro thread lo bloccherà immediatamente e inizierà a funzionare. L'analogia con l'insegna della porta del bagno è perfetta.

"Allora come lavoro con un mutex? Devo creare oggetti speciali?"

"È molto più semplice di così. I creatori di Java hanno integrato questo mutex nella classe Object. Quindi non devi nemmeno crearlo. Fa parte di ogni oggetto. Ecco come funziona:"

Codice Descrizione
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Il metodo swap scambia i valori delle variabili name1 e name2.

Cosa potrebbe accadere se viene chiamato da due thread contemporaneamente?

Esecuzione effettiva del codice Codice del primo thread Codice del secondo thread
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 linea di fondo
I valori delle variabili sono stati scambiati due volte, tornando ai loro posti originali.

Prestare attenzione alla parola chiave  sincronizzato .

"Sì, cosa significa?"

"Quando un thread entra in un blocco di codice contrassegnato come sincronizzato, la macchina Java blocca immediatamente il mutex dell'oggetto indicato tra parentesi dopo la parola sincronizzato. Nessun altro thread può entrare in questo blocco finché il nostro thread non lo lascia. Non appena il nostro thread esce il blocco contrassegnato come sincronizzato, il mutex viene immediatamente e automaticamente sbloccato e sarà disponibile per essere acquisito da un altro thread."

Se il mutex è occupato, il nostro thread rimarrà fermo e aspetterà che si liberi.

"Così semplice e così elegante. È una bellissima soluzione."

"Sì. Ma cosa pensi che accadrà in questo caso?"

Codice Descrizione
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;
}
}
}
I metodi swap e swap2 condividono lo stesso mutex (l' oggetto this ).

Cosa succede se un thread chiama il metodo swap e un altro thread chiama il metodo swap2?

"Poiché il mutex è lo stesso, il secondo thread dovrà attendere fino a quando il primo thread lascerà il blocco sincronizzato. Quindi non ci saranno problemi con l'accesso simultaneo."

"Ben fatto, Amigo! Questa è la risposta corretta!"

Ora vorrei sottolineare che la sincronizzazione può essere utilizzata per contrassegnare non solo blocchi di codice, ma anche metodi. Ecco cosa significa:

Codice Cosa succede davvero
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;
}
}