synchronized ist ein Schlüsselwort, das einen thread-sicheren Zugriff auf kritische Bereiche des Codes ermöglicht. Es kann entweder für eine Methode verwendet werden (zum Beispiel public synchronized void foo()), oder für einen Codeblock (synchronized(obj) { ... }). Wenn ein Thread in einen synchronized-Block eintritt, erhält er den Monitor (Lock) des Objekts. Solange der Monitor besetzt ist, können andere Threads nicht in einen anderen synchronized-Block eintreten, der dasselbe Objekt als Monitor verwendet.
Besonderheiten:
this, ein statisches Objekt (zum Beispiel über die Klasse) oder ein beliebiges Objekt synchronisieren.public class Counter { private int count; private final Object lock = new Object(); // privates Objekt public void increment() { synchronized(lock) { count++; } } }
Warum ist es besser, ein privates Lock zu verwenden? Weil, wenn man sich über ein öffentliches Objekt (zum Beispiel this oder eine öffentliche Zeichenkette) synchronisiert, kann externen Code auch diesen Monitor erlangen, was zu Deadlocks oder fehlerhaften Operationen führen kann.
Frage: Was passiert, wenn man sich über ein Objekt vom Typ String mit einem festen Wert synchronisiert?
Antwort: Zeichenfolgen in Java sind intern (dasselbe Objekt für identische Literale). Wenn man sich über eine Zeichenfolge wie synchronized("lock") synchronisiert, kann man zufällig mit einem anderen Code in Konflikt geraten, der sich über dasselbe Literal synchronisiert, was zu unerwarteten Blockierungen zwischen völlig unterschiedlichen Teilen des Programms führt.
synchronized("LOCK") { ... }
Geschichte
In einem multithreaded Handelssystem wurden öffentliche Objekte zur Synchronisation verwendet, und externen Code gelang es, den Lock zu erfassen, was zu temporären Deadlocks zwischen Threads verschiedener Module und Ausfällen an der Börse führte.
Geschichte
Ein junger Entwickler synchronisierte den Zugriff auf eine Sammlung über einen Zeichenfolgenliteral. Ein anderer Teil des Codes synchronisierte sich ebenfalls über eine Zeichenfolge mit demselben Wert. Diese Threads stellten sich hintereinander auf, was zu einer drastischen Verlangsamung der Geschäftslogik führte.
Geschichte
Für die Synchronisation wurde jedes Mal ein neues Objekt gewählt:
synchronized(new Object()) { ... }. Infolgedessen funktionierte die Synchronisation überhaupt nicht, und verschiedene Threads hatten gleichzeitig Zugriff auf die Daten. Dies wurde erst bei einem Lasttest entdeckt.