Completable Futures: Java 9 Update

Wie im vorherigen Artikel zu dem neuen Publish-Subscribe-Framework in Java 9 schon erwähnt, gibt es immer wieder kleinere oder größere Änderungen an den Concurrency APIs im JDK. In Java 9 gab es neben der größeren Neuerung bezüglich der Flow-API auch noch einige kleinere Änderungen im Bezug auf die CompletableFuture-API. Die wichtigsten sollen hier aufgeschlüsselt werden.

Timeouts

Fangen wir gleich an. Zunächst hat die Klasse CompletableFuture die zusätzliche Methode completeOnTimeout bekommen.

CompletableFuture<String> future = new CompletableFuture<>();

// blockiert für 500 Millisekunden oder bis future abgeschlossen:
future.completeOnTimeout("Fallback", 500, TimeUnit.MILLISECONDS);

System.out.println(future.get());

Da der CompletableFuture aus Zeile 1 nicht durch einen Aufruf der complete-Methode abgeschlossen wird, blockiert die completeOnTimeout-Methode in Zeile 4 den aktuellen Thread für 500 Millisekunden. Da der CompletableFuture weder vor, noch während dieser 500 Millisekunden abgeschlossen wird, wird der Wert anschließend auf “Fallback” gesetzt. Dieser Wert wird in der letzten Zeile ausgegeben.

Alternativ dazu kann auch eine TimeoutException ausgegeben werden:

CompletableFuture<String> future = new CompletableFuture<>();
future.orTimeout(500, TimeUnit.MILLISECONDS);
Thread.sleep(1000);
future.complete("Too late.");
System.out.println(future.get()); // TimeoutException

Hier wird die Methode orTimeout verwendet. Diese blockiert den aktuellen Thread nicht. Stattdessen wird in Zeile 3 eine Sekunde lang gewartet, um dann den Future mit dem String “Too late.” abzuschließen. Da das Timeout von 500ms jedoch schon abgelaufen ist, kommt es beim Auslesen des Futures in Zeile 5 zu einer TimeoutException.

Asynchrone Future Completion

Ein weiteres neues Feature der CompletableFuture-API mit Java 9 ist die Einführung der completeAsync-Methode.

CompletableFuture<String> future = new CompletableFuture<>();
future.completeAsync(() -> "World");
System.out.println(future.get());

Der einzige Unterschied zum Aufruf der sonst üblichen complete-Methode ist, dass der Future hier asynchron abgeschlossen wird. Als Argument kann dabei ein Objekt vom Typ Runnable übergeben werden. Dessen run-Methode wird dann asynchron ausgeführt.

Dies lässt sich auch mit dem neuen DelayedExecutor kombinieren:

CompletableFuture<String> future = new CompletableFuture<>();
future.completeAsync(() -> "World", 
  CompletableFuture.delayedExecutor(500, TimeUnit.MILLISECONDS));
future.completeOnTimeout("No World", 400, TimeUnit.MILLISECONDS);

System.out.println(future.get());

Dieses Snippet ist ähnlich zum zweiten Snippet oben, jedoch wird hier asynchron 500 Millisekunden gewartet, bevor der Future abgeschlossen wird. Da der completeOnTimeout-Aufruf jedoch schon nach 400 Millisekunden den Future abschließt, lautet die Ausgabe:
No World

Dieses Ergebnis der completeOnTimeout-Methode lässt sich auch von der completeAsync-Methode nicht mehr überschreiben.

CompletableFutures kopieren mit copy()

Manchmal sollen defensive Kopien eines Futures angelegt werden, oder Futures an einer Schnittstelle zurückgegeben werden, die nicht von außen abschließbar sein sollen. Dafür gibt es jetzt die copy-Methode:

CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> future2 = future.copy();

future.complete("Test"); // beide werden abgeschlossen
System.out.println(future.get());
System.out.println(future2.get());

Hier wird also eine neue Instanz vom Typ CompletableFuture unter Zuhilfenahme der copy-Methode erzeugt (Zeile 2). Wenn anschließend der originale Future abgeschlossen wird (Zeile 4), wird der kopierte Future ebenfalls mit abgeschlossen. Die Ausgabe lautet:
Test
Test

Wenn dagegen der zweite Future mit einem Aufruf der complete-Methode abgeschlossen wird, wird der erste nicht automatisch mit abgeschlossen:

...
future2.complete("test");
System.out.println(future.get()); //terminiert nicht
...

Dadurch können an einer Schnittstelle nun auch quasi unidirektionale Futures zurückgegeben werden, die sich von außen nicht direkt abschließen lassen und auf die nur “gehorcht” werden soll.

Detaillierte Informationen zu allen neuen Methoden und den beiden hier nicht besprochenen Methoden minimalCompletionStage und der Factory-Methode newIncompleteFuture können der offiziellen Javadoc entnommen werden.

Short URL for this post: https://wp.me/p4nxik-32f
This entry was posted in Java Basics and tagged , , , . Bookmark the permalink.

Leave a Reply