Vom Chaos zur Ordnung: Das Verständnis strukturierter Nebenläufigkeit in Java…

Vom Chaos zur Ordnung: Das Verständnis strukturierter Nebenläufigkeit in Java
Einführung
In der Regel wird Komplexität in der Programmierung durch die Aufteilung von Aufgaben in Unter-Aufgaben bewältigt. Diese Unter-Aufgaben können dann gleichzeitig ausgeführt werden.
Seit Java 5 hilft die ExecutorService API Programmierern dabei, diese Unter-Aufgaben nebeneinander auszuführen. Aufgrund der Natur der parallelen Ausführung kann jedoch jede Unter-Aufgabe unabhängig voneinander erfolgreich oder fehlerhaft sein, ohne dass eine implizite Kommunikation zwischen ihnen besteht. Das Scheitern einer Unter-Aufgabe führt nicht automatisch zur Stornierung der anderen Unter-Aufgaben. Obwohl versucht werden kann, diese Stornierung manuell über eine externe Behandlung zu verwalten, ist dies sehr schwierig – insbesondere wenn eine große Anzahl von Unter-Aufgaben beteiligt ist. Dies könnte potenziell zu losen Threads führen (auch als Thread-Leaks bekannt). Obwohl Virtual Threads eine kostengünstige Möglichkeit sind, jedem (Unter-)Aufgabe einen Thread zuzuweisen, bleibt die Verwaltung der Ergebnisse eine Herausforderung.
Ein ExecutorService ermöglicht es einem Thread, diesen zu erstellen und andere Threads, Aufgaben an diesen ExecutorService zu senden. Die Threads, die die Aufgaben ausführen, haben keine Beziehung zu beiden dieser Threads. Darüber hinaus kann ein völlig anderer Thread auf das Ergebnis der Ausführung dieser Unter-Aufgaben warten, indem er auf das Future-Objekt verweist, das sofort nach der Aufgabenübermittlung an den ExecutorService bereitgestellt wird. Somit muss die Unter-Aufgabe nicht zur Aufgabe zurückkehren, die sie übermittelt hat. Sie kann möglicherweise zu mehreren Threads oder zu keinem zurückkehren.
Auch die Beziehung zwischen Aufgabe und ihrer Unter-Aufgabe ist nur logisch und nicht in der Code-Struktur sichtbar. Es gibt keine Durchsetzung oder Verfolgung der Beziehung zwischen Aufgabe und Unter-Aufgaben zur Laufzeit.
Strukturierte Nebenläufigkeit in Java zielt darauf ab, all diese Herausforderungen zu lösen, indem sie:
– die Stornierung von Unter-Aufgaben zuverlässig automatisiert und Thread-Leaks und Verzögerungen vermeidet
– sicherstellt, dass eine (Unter-)Aufgabe nur zum Thread zurückkehrt, der sie übermittelt hat
– eine strukturierte Beziehung zwischen Aufgabe und ihren Unter-Aufgaben erzwingt – die auch verschachtelt sein können
Unstrukturierte Nebenläufigkeit
Lassen Sie uns mehr über die aktuelle Situation der unstrukturierten Nebenläufigkeit in Java erfahren. Betrachten Sie eine Funktion handle(), die Benutzerinformationen und seine zugehörige Bestellung abruft.
Response handle() throws ExecutionException, InterruptedException {
Future<String> user = esvc.submit(() -> findUser());
Future<Integer> order = esvc.submit(() -> fetchOrder());
String theUser = user.get(); // Join findUser
int theOrder = order.get(); // Join fetchOrder
return new Response(theUser, theOrder);
}
Auf den ersten Blick scheint der Code einfach zu sein und das zu tun, was er beabsichtigt. Bei genauerer Betrachtung können jedoch mehrere Probleme identifiziert werden: