ProgrammierungJava Mittel-/Backend-Entwickler

Was wissen Sie über die Arbeit mit Threads in Java und wie organisiert man die Thread-Sicherheit?

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

Antwort.

In Java wird die Arbeit mit Threads über die Klassen Thread, Runnable und aus dem Paket java.util.concurrent realisiert. Zur Organisation der Thread-Sicherheit werden verschiedene Synchronisationsmechanismen verwendet:

  • Synchronisierte Blöcke/Methoden (synchronized) ermöglichen es mehreren Threads, auf gemeinsame Daten ohne Wettbewerb zuzugreifen.
  • volatile gewährleistet die Sichtbarkeit von Änderungen an einer Variablen zwischen Threads.
  • Klassen aus java.util.concurrent (z. B. ReentrantLock, Semaphore, AtomicInteger, ConcurrentHashMap) bieten flexiblere Möglichkeiten zur Synchronisation.

Beispiel für Synchronisation:

public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }

Beispiel mit Verwendung von atomaren Klassen:

import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }

Fangfrage.

Gewährleistet das Schlüsselwort volatile die Thread-Sicherheit beim Inkrementieren eines int-Zählers?

Antwort: Nein. volatile gewährleistet nur die Sichtbarkeit des Wertes zwischen Threads, jedoch nicht die Atomarität der Operationen. Inkrementieren ist keine atomare Operation (count++ besteht aus Lesen, Erhöhen und Schreiben), daher können Daten verloren gehen. Für einen thread-sicheren Inkrement benötigen Sie Synchronisation oder Klassen wie AtomicInteger.

volatile int count = 0; // count++ ist nicht threadsicher!

Beispiele für echte Fehler aufgrund von Unkenntnis der Feinheiten des Themas.


Geschichte

In einem Online-Shop wurde der Bonuszähler über einen volatile-Zähler aktualisiert. Unter der Last von Tausenden von Benutzern, die Produkte bestellten, gingen einige Boni aufgrund von Thread-Race-Bedingungen bei count++ verloren.


Geschichte

Ein Mitarbeiter verwendete eine normale ArrayList als gemeinsamen Puffer zwischen Producer und Consumer in einer Multithreading-Anwendung, was zu ConcurrentModificationException führte. Die Lösung bestand darin, synchronisierte Blöcke zu verwenden oder auf CopyOnWriteArrayList umzusteigen.


Geschichte

Bei der Zahlungsabwicklung führte der Entwickler die Operation "überprüfen und abheben" über zwei nicht verbundene Methoden durch. Unter hoher Last führte dies zu doppelten Abbuchungen, bis atomare Transaktionen und Sperren implementiert wurden.