Nachdem Oracle mit der Einführung der „Stream API“ in Java 8 (pkg: java.util.stream) eine Grundlage in Richtung Reaktive Programmierung gesetzt hatte, ist mit dem neuen Flow-API in JAVA 9 das erste Instrument für eine Reaktive Programmierung in der Core-API verfügbar. Somit soll ein Programmierparadigma, das in den letzten 10-15 Jahren zunehmend an Bedeutung gewonnen hat, auch mit der Java Standardbibliothek direkt umsetzbar sein und die Entwicklung Reaktiver Systeme, als Basis der zukünftigen Softwarearchitektur, ermöglichen. Aber der Reihe nach – was ist überhaupt Reaktive Programmierung?
Reaktive Programmierung handelt prinzipiell von der Verarbeitung asynchroner Datenströme, wobei die Applikation auf die Daten reagiert wenn diese eintreffen. Um einen Überblick über die wichtigsten Aspekten asynchroner Datenströme zu bekommen ist ein Vergleich mit deren Gegenteil, den synchronen Datenströmen, sicherlich hilfreich.
Ein Datenaustausch zwischen der Datenquelle und dem Datenkonsumenten erfolgt in synchroner Weise, wenn der Datenversand und der Datenempfang im selben Zeitintervall erfolgen. Damit dieses Zeitintervall zwischen den beiden Parteien abgestimmt wird, kommunizieren diese im Vorfeld des Datenaustausches miteinander und „vereinbaren“ die Parameter, welche die Abstimmung des Zeitintervalls definieren, wie z.B.:
-
Festlegung der verwendeten Uhr (beide Parteien benutzen die selbe Uhr=Synchronisation)
-
Definition von Beginn und Ende des Datenaustausches,
-
Bestimmung welche der Parteien den Transfer kontrolliert, usw.
Hieraus ableitend, liegen einige der Nachteile des synchronen Datenaustausches auf der Hand:
-
Die Parteien sind während der vereinbarten Transferzeit für andere Operationen nicht verfügbar (es findet „blocking“ statt)
-
Der Datenempfänger kann u.U. viel mehr Daten zugestellt bekommen, als er in dem Zeitintervall verarbeiten kann.
Der asynchrone Datenaustausch dagegen sieht vor, dass die Datenquelle und der Datenkonsument den Datenaustausch zeitlich unabhängig voneinander abwickeln. Um dies zu realisieren, sendet der Datenkonsument eine Anfrage („request“) an die Datenquelle in der die maximale Menge an angeforderten Daten definiert ist. Erst wenn die angeforderten Daten verfügbar sind, sendet die Datenquelle diese an den Datenkonsument zurück. Dabei muss die Datenquelle mit dem Datentransfer nicht warten bis alle angefordeten Daten verfügbar sind, sondern sie kann die Daten einer Anfordeung auch in mehreren Blöcken versenden (im eigenen Ermessen). Damit der Datenkonsument die Daten empfangen kann wird vor dem eigentlichen Datentransfer von der Datenquelle ein Signal gesendet, mit dem das Eintreffen der Daten bekanntgegeben wird.
Bild 1. Prinzipien des asynchronen Datentransfers
Die Voraussetzung dafür ist, dass der Datenkonsument als Observer bei der Datenquelle registriert ist.
Anhand dieses Auszugs einiger der wichtigen Aspekten asynchronen Datentransfers kann nachvollzogen werden, wie es um die Effizienz der beiden Parteien steht: Zwischen der Datenanforderung und dem eigentlichen Datentransfer sind die Parteien für andere Operationen nicht geblockt und können die Ressourcen effizienter verwenden. Des Weiteren ist durch die Tatsache, dass der Datenkonsument selbst die maximale Datenmenge in der Anforderung bestimmt, die Entstehungswahrscheinlichkeit eines „bottle necks“ stark reduziert, bzw. zumindest vom Datenkonsument selbst abhängig gemacht.
Die Flow API der Java SE 9 Version bietet die Grundlagen für Reaktive Programmierung und orientiert sich dabei vollständig an den de facto Standards der Reaktiven Programmierung, festgelegt durch die Initiative „reactive streams“ in Form eines Manifests. Sie besteht aus einer einzigen finalen Klasse (java.util.concurrent.Flow), die lediglich 4 statische Interfaces anbietet:
public final class Flow { public static interface Publisher<T> { public void subscribe(Subscriber<? super T> subscriber); } public static interface Subscriber<T> { public void onSubscribe(Subscription subscription); public void onNext(T item); public void onError(Throwable throwable); public void onComplete(); } public static interface Subscription { public void request(long n); public void cancel(); } public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> {} }
Die Flow API definiert die Schnittstellen für die grundlegenden Elemente zur reaktiven Programmierung: Subscriber, Publisher und Subscription. Um die Logik der Datenverarbeitung auf dem Weg vom Publisher zum Subscriber zu isolieren, bietet die Flow-API auch die Schnittstelle Processor, welche sowohl Subscriber als auch und Publisher beerbt und gleichzeitig auf diese beiden Typen parametrisiert ist.
Bild 2. Die Grundelemente der Flow API
Da sich der Processor zwischen dem Subscriber und dem Publisher befindet, liegt es auf der Hand, dass er sowohl Subscriber (für den ursprünglichen Publisher) als auch Publisher (für den ursprünglichen Subscriber) darstellen muss, um den Datenfluss zwischen den beiden Endpunkten zu gewährleisten. Vom anderen Standpunkt betrachtet wiederum, vereint die neue Flow-API Teile zweier etablierter Patterns: Iterator und Observer/Observable. Indem der Subscriber die Menge an Daten vom Publisher anfordert, verhält er sich als Iterator und „zieht“ die Daten vom Publisher („pull“ – Model), während der letztere wie ein Observable die Daten zum Subscriber „schiebt“ (push-Model).
Der Vollständigkeit halber soll an der Stelle erwähnt werden, dass nur eine einzige Implementierung des und Publisher-Interfaces der Java SE9 API mit der Klasse „SubmissionPublisher<T>“ vorhanden ist.
Die Flow API ist in der jetzigen Version lediglich eine interoperationale Spezifikation und keine Endbenutzer-API. Es bleibt abzuwarten wann und in welcher Form die aktuell bestehenden und etablierten reaktiven Bibliotheken (RxJava, Akka, usw.) auf die neue Java Flow API umsteigen.
Die wesentlichen Vorteile der Verwendung von asynchronen Datenströmen wurden in der Einleitung bearbeitet, wer sich jedoch in weiterführenden Details zur Reaktiven Programmierung interessiert, ist, für den Einstieg, mit den unten aufgeführten Quellen gut aufgehoben.
Links:
- The introduction to Reactive Programming you’ve been missing
- 5 Things to Know About Reactive Programming
- Notes on Reactive Programming Part I: The Reactive Landscape
- Difference Between Synchronous and Asynchronous Data Transfer
-
Bücher:
-
Functional Reactive Programming (ISBN-13: 978-1633430105 )
-
Reactive Design Patterns (ISBN-13: 978-1617291807 )