© Excellent backgrounds/Shutterstock.com
Kolumne: Java-Trickkiste

Kolumne: Java-Trickkiste


Die letzten beiden Kolumnen haben seiteneffektfreie Programmierung mit Java vorgestellt und gezeigt, wie Code dadurch besser les- und testbar werden kann. Sogar die Performance kann gewinnen, wenn man unveränderliche („persistente“) Collections mit passenden Algorithmen verwendet.

Dieses Mal betrachten wir seiteneffektfreien Code – d. h. hier konkret Klassen, bei denen alle Felder final sind – im Kontext mehrerer Threads.

Threadsichere Zugriffe

Wenn mehrere Threads auf dieselben Daten zugreifen, müssen sie die Regeln des Java Memory Model (JMM) beachten [1], sonst kommt es zu subtilen, nur sporadisch auftretenden Fehlern. Das kann sich zum Beispiel so äußern, dass verschiedene Threads unterschiedliche Werte sehen, je nachdem auf welchem Prozessorkern sie gerade laufen. Für unveränderliche Datenstrukturen garantiert das JMM, dass jeder Thread sie also vollständig initialisiert sieht.

Nehmen wir an, mehrere Threads verarbeiten Datensätze und wollen eine gemeinsame Statistik darüber führen, wie viele Datensätze erfolgreich verarbeitet wurden und bei wie vielen Fehler aufgetreten sind. Listing 1 zeigt eine unveränderliche Datenstruktur, die für diese beiden Werte je ein int-Feld hat. Lesende Zugriffe auf diese Datenstruktur sind sicher, egal wie viele Threads sie parallel benutzen.

Listing 1

public classs { private final int numSuccess; private final int numFailure; public Statistics (int numSuccess, int numFailure) { this.numSuccess = numSuccess; this.numFailure = numFailure } public int getNumSuccess() { return numSuccess; } public int getNumFailure() { return numFailure; } }

Spannend wird es, wenn die Counter aktualisiert werden sollen. Denn das tun ja mehrere Threads parallel, und der Code muss Kollisionen vermeiden und allgemein die Regeln des JMM beachten. Listing 2 zeigt eine typische Implementierung.

Listing 2

public class ccess { private final AtomicReference<Statistics> ref = new AtomicReference<> (new Statistics (0, 0)); public Statistics get() { return ref.get(); } public void increment (int numSuccess, int numFailure) { Statistics before, after; do { before = ref.get(); after = new Statistics ( before.getNumSuccess() + numSuccess, before.getNumFailure() + numFailure); } while (! ref.compareAndSet (before, after)); } }

Die Klasse StatisticAccess kapselt den Zugriff auf die Statistics: Alle Threads, die lesend oder schreibend darauf zugreifen wollen, teilen sich eine Instanz. Sie enthält im Kern eine java.util.concurrent.AtomicReference auf das...

Exklusives Abo-Special

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang