CompletableFuture
Diesen Monat behandelt die Kolumne die Klasse CompletableFuture, die seit Java 8 Teil der Standardbibliothek ist. Sie bietet ein API, mit dem man asynchrone Tasks starten, kombinieren und allgemein verwalten kann. Sie ist ein leistungsstarkes Hilfsmittel, besonders, wenn man reaktive Systeme bauen will [1].
Arno Haase
Klassische FuturesDer Aufrufer erhält dann eine Future-Instanz, die als Platzhalter für das Ergebnis dient. Er kann das Ergebnis zu einem beliebigen Zeitpunkt abfragen, indem er get() aufruft. Dieser Aufruf blockiert, bis die asynchrone Berechnung abgeschlossen ist.Listing 1ExecutorService ec = Executors.newCachedThreadPool ();Future gaussian = ec.submit ((Callable) () -> new Random ().nextGaussian ());System.out.println (gaussian.get ());ExecutorService ec = Executors.newCachedThreadPool ();Future gaussian = ec.submit ((Callable) () -> new Random ().nextGaussian ());System.out.println (gaussian.get ());Außerdem bieten Futures keine Unterstützung für Verarbeitungsketten oder Kombinationen von Ergebnissen. Wenn man z. B. parallel eine Reihe von Werten aus der Datenbank nachschlagen will, um sie dann gemeinsam zu verarbeiten und das Ergebnis über einen Web Service weiterzuleiten, braucht man einen synchron blockierenden Steuerungsthread. Zumindest, wenn man mit klassischen Futures arbeitet.CompletableFutures kombinierenZur Illustration erzeugt Listing 2 asynchron eine Million Gauß-verteilte Zufallszahlen, quadriert jede von ihnen, addiert die Ergebnisse auf und gibt das Ergebnis aus. Der aufrufende Thread initialisiert die Verarbeitungskette dabei, ist aber anschließend überhaupt nicht mehr am Ablauf beteiligt.Listing 2final List> squared = new ArrayList (); for (int i=0; i new Random ().nextGaussian ()). thenApplyAsync ((x) -> x*x) );} CompletableFuture result = CompletableFuture.completedFuture (0.0); for (CompletableFuture f: squared) { result = result.thenCombineAsync (f, (f1, f2) -> f1 + f2);} result.thenAcceptAsync (System.out::println); System.out.println ("Warte...");Thread.sleep (5_000);final List> squared = new ArrayList (); for (int i=0; i new Random ().nextGaussian ()). thenApplyAsync ((x) -> x*x) );} CompletableFuture result = CompletableFuture.completedFuture (0.0); for (CompletableFuture f: squared) { result = result.thenCombineAsync (f, (f1, f2) -> f1 + f2);} result.thenAcceptAsync (System.out::println); System.out.println ("Warte...");Thread.sleep (5_000);Auf diesem CompletableFuture wird gleich die Methode thenApplyAsync aufgerufen, um die Zufallszahl...
CompletableFuture
Diesen Monat behandelt die Kolumne die Klasse CompletableFuture, die seit Java 8 Teil der Standardbibliothek ist. Sie bietet ein API, mit dem man asynchrone Tasks starten, kombinieren und allgemein verwalten kann. Sie ist ein leistungsstarkes Hilfsmittel, besonders, wenn man reaktive Systeme bauen will [1].
Arno Haase
Klassische FuturesDer Aufrufer erhält dann eine Future-Instanz, die als Platzhalter für das Ergebnis dient. Er kann das Ergebnis zu einem beliebigen Zeitpunkt abfragen, indem er get() aufruft. Dieser Aufruf blockiert, bis die asynchrone Berechnung abgeschlossen ist.Listing 1ExecutorService ec = Executors.newCachedThreadPool ();Future gaussian = ec.submit ((Callable) () -> new Random ().nextGaussian ());System.out.println (gaussian.get ());ExecutorService ec = Executors.newCachedThreadPool ();Future gaussian = ec.submit ((Callable) () -> new Random ().nextGaussian ());System.out.println (gaussian.get ());Außerdem bieten Futures keine Unterstützung für Verarbeitungsketten oder Kombinationen von Ergebnissen. Wenn man z. B. parallel eine Reihe von Werten aus der Datenbank nachschlagen will, um sie dann gemeinsam zu verarbeiten und das Ergebnis über einen Web Service weiterzuleiten, braucht man einen synchron blockierenden Steuerungsthread. Zumindest, wenn man mit klassischen Futures arbeitet.CompletableFutures kombinierenZur Illustration erzeugt Listing 2 asynchron eine Million Gauß-verteilte Zufallszahlen, quadriert jede von ihnen, addiert die Ergebnisse auf und gibt das Ergebnis aus. Der aufrufende Thread initialisiert die Verarbeitungskette dabei, ist aber anschließend überhaupt nicht mehr am Ablauf beteiligt.Listing 2final List> squared = new ArrayList (); for (int i=0; i new Random ().nextGaussian ()). thenApplyAsync ((x) -> x*x) );} CompletableFuture result = CompletableFuture.completedFuture (0.0); for (CompletableFuture f: squared) { result = result.thenCombineAsync (f, (f1, f2) -> f1 + f2);} result.thenAcceptAsync (System.out::println); System.out.println ("Warte...");Thread.sleep (5_000);final List> squared = new ArrayList (); for (int i=0; i new Random ().nextGaussian ()). thenApplyAsync ((x) -> x*x) );} CompletableFuture result = CompletableFuture.completedFuture (0.0); for (CompletableFuture f: squared) { result = result.thenCombineAsync (f, (f1, f2) -> f1 + f2);} result.thenAcceptAsync (System.out::println); System.out.println ("Warte...");Thread.sleep (5_000);Auf diesem CompletableFuture wird gleich die Methode thenApplyAsync aufgerufen, um die Zufallszahl...