© Julia She/Shutterstock.com, © sezer66/Shutterstock.com
Concurrency in Java

Das Kreuz mit der Parallelität


Das Thema Concurrency – auch Nebenläufigkeit genannt – ist für viele die hohe Kunst der Java-Programmierung. Bisher mussten sich nicht viele Entwickler mit parallelen Threads herumschlagen. Aber suchen Sie heutzutage mal ein technisches Gerät, das nicht mit mehreren Prozesskernen arbeitet! Sie tragen wahrscheinlich in der Hosentasche ständig eines mit sich herum. Wir werfen einen Blick auf die Grundlagen von Code, der für die parallele Verarbeitung optimiert ist, und zeigen, welche ersten Hürden es zu nehmen gilt.

Mein bester Freund John Green erhielt an der Uni drei Jahre in Folge einen Preis für Informatik. Bis dahin hatte das niemand zuvor geschafft. Ich bin mir auch nicht sicher, ob sein Rekord bisher gebrochen wurde. Entweder war John außergewöhnlich begabt oder der Rest von uns war einfach nicht so schlau. Ich hoffe, es ist Ersteres. Einmal, und nur einmal, habe ich es geschafft, in einer Prüfung eine höhere Punktzahl als John zu erreichen. In einem Kurs ging es um paralleles und nebenläufiges Programmieren. Ein Thema, das ich sehr interessant fand. Ich habe mich also besser vorbereitet als für die meisten Tests zuvor, während John kaum zu irgendeiner der Vorlesungen ging. Als der Tag der Abrechnung kam, begannen wir beide wie wild zu schreiben. Die Prüfung bestand aus vier Aufgaben, und unser Professor MacGregor wies uns an, drei auszuwählen, die wir bearbeiten wollten. Ich tat mein Bestes, und am Ende erreichte ich etwa 92 Prozent der möglichen Punkte. Bei der anschließenden Diskussion des Tests fragte mich John: „Welche zwei Abschnitte hast du beantwortet?“ Ich war sicher nicht der beste Student. Ich war auch nicht Nummer Zwei oder Drei. Aber Concurrency und Parallelität faszinierten mich. Es war das einzige Mal, dass ich die Nummer Eins war.

Concurrency ist ein Thema, das nicht jedem liegt. Es erfordert, dass man mit vielen Unwägbarkeiten arbeiten muss, was den eigenen Code betrifft. Eine Single-Thread-Routine kann man einfach von oben nach unten lesen. Und abgesehen von ein paar kniffligen if-else-Konstruktionen und -Schleifen, ist es normalerweise ziemlich offensichtlich, was der Code macht. Besonders bei Java-Code. Denn dort gilt „What you see is what you get“. Bei Concurrency ist alles offen. Schauen Sie sich z. B. den Code aus Listing 1 an.

Listing 1: Was kann schiefgehen?

import java.lang.ref.*; import java.util.concurrent.atomic.*; class Resource { private static ExternalResource[] externalResourceArray = new ExternalResource[128]; private static final AtomicInteger next = new AtomicInteger(); private final int myIndex; Resource(ExternalResource resource) { myIndex = next.getAndIncrement() & 127; externalResourceArray[myIndex] = resource; // ... } protected void finalize() { externalResourceArray[myIndex] = null; } public void action() { int i = myIndex; Resource.update(externalResourceArray[i]); } private static void update(ExternalResource ext) { ext.status = 42; } }

Was könnte hier schiefgehen? Das ist leider überhaupt nicht offensichtlich. In Java können Objekte manchmal vorzeitig finalisiert werden. Da die Methode finalize() von einem Hintergrundthread aufgerufen wird, könnte es passieren, dass ein anderer Thread immer noch action()aufruft. Der obige Code könnte also eine NullPointerException verursachen.

Java-9-Tipp

In Java 9 haben wir die Möglichkeit, das vorzeitige Finalisieren von Objekten durch eine Barriere zu verhindern, den reachabilityFence:

public void action() { try { int i = myIndex; Resource.update(externalResourceArray[i]); } finally { Reference.reachabilityFence(this); } }

Das ist leider wenig intuitiv. Die meisten Entwickler würden reachabilityFence so schreiben:

 public void action() { int i = myIndex; Reference.reachabilityFence(this); Resource.update(externalResourceArray[i]); }

Das wäre jedoch nicht korrekt, da es den Finalizer-Thread nicht davon abhalten würde, direkt nach dem Aufruf von reachabilityFence() und vor unserem Aufruf von update loszulegen. Und wieder käme es zu einer NullPointerException.

Wir machen auch in einem weiteren Bereich Fehler: Wir überlegen, was für andere Threads sichtbar sein sollte, ohne über das Java Memory Model nachzudenken. Bevor ich hier jedoch in die Tiefe gehe, möchte ich einen Gang runterschalten und ein ...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang