synchronized è una parola chiave che consente l'accesso thread-safe a sezioni critiche del codice. Può essere utilizzata sia per un metodo (ad esempio, public synchronized void foo()), sia per un blocco di codice (synchronized(obj) { ... }). Quando un thread entra in un blocco synchronized, ottiene un monitor (lock) dell'oggetto. Mentre il monitor è occupato, gli altri thread non possono entrare in un altro blocco synchronized che utilizza lo stesso oggetto come monitor.
Caratteristiche:
this, su un oggetto statico (ad esempio, sulla classe) o su un oggetto arbitrario.public class Counter { private int count; private final Object lock = new Object(); // oggetto privato public void increment() { synchronized(lock) { count++; } } }
Perché è meglio usare un lock privato? Perché se si sincronizza su un oggetto pubblico (ad esempio, su this o su una stringa pubblica), il codice esterno potrebbe anche acquisire questo monitor, portando a deadlock o comportamenti scorretti.
Domanda: Cosa succede se ci si sincronizza su un oggetto di tipo String che contiene un valore fisso?
Risposta: Le stringhe in Java sono internate (gli stessi oggetti per letterali identici). Se ci si sincronizza su una stringa di tipo synchronized("lock"), si potrebbe incrociarsi accidentalmente con altro codice che si sincronizza sullo stesso letterale, portando a blocchi inattesi tra parti completamente diverse del programma.
synchronized("LOCK") { ... }
Storia
In un sistema di trading multi-thread, sono stati utilizzati oggetti pubblici per la sincronizzazione e il codice esterno è riuscito ad acquisire il lock, portando a deadlock temporanei tra thread di moduli diversi e fermi in borsa.
Storia
Un giovane sviluppatore ha sincronizzato l'accesso a una collezione su un letterale stringa. Un'altra parte del codice si è sincronizzata anche su una stringa con lo stesso valore. Questi thread si sono messi in coda l'uno dietro l'altro, causando un rallentamento improvviso della logica di business.
Storia
Per la sincronizzazione è stato scelto ogni volta un nuovo oggetto:
synchronized(new Object()) { ... }. Di conseguenza, la sincronizzazione non funzionava affatto e diversi thread avevano accesso contemporaneo ai dati. Questo è stato scoperto solo durante il test di carico.