ProgrammierungBackend-Entwickler

Beschreiben Sie, wie ein synchronized-Block in Java funktioniert. Was sind seine Besonderheiten, wie wählt man ein Objekt zur Synchronisation und wie kann eine falsche Auswahl zu Fehlern führen?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

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:

  • Die Synchronisation über dasselbe Objekt gewährleistet wechselseitigen Ausschluss (nur ein Thread kann den Block gleichzeitig ausführen).
  • Man kann sich entweder über this, ein statisches Objekt (zum Beispiel über die Klasse) oder ein beliebiges Objekt synchronisieren.
  • Wenn man ein Objekt als Monitor auswählt, das von mehreren Threads zugänglich ist, oder umgekehrt, ein zu lokales Objekt, kann man die Thread-Sicherheit gefährden.

Beispiel zur Auswahl des Synchronisationsobjekts

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.

Fangfrage.

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.

Beispiel (so sollte man es nicht machen):

synchronized("LOCK") { ... }

Beispiele für reale Fehler aufgrund fehlenden Wissens über die Feinheiten des Themas.


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.