Parallele Programmierung (3IB)
=> Grundlage: Hettel und Tran (2016, Kap. 15)
Future
CompletableFuture
(Framework)
CompletableFuture
x
, y
) mit gleichmäßiger Verteilung\[\frac{\mbox{Anzahl der Punkte innerhalb und auf dem Kreis}}{\mbox{Anzahl aller Punkte}} = \frac{\frac{\pi r^2}{4}}{r^2}\]
pi = 4.0 * in / (in + out)
in
: Anzahl der Punkte innerhalb des Kreises (sqrt(x*x + y*y) <= 1
)out
: die restlichen, die außerhalb des Kreises liegen (sqrt(x*x + y*y) > 1
)pp.08.01-ConcurrencyMonteCarloPiSeq
Future
”pp.08.02-ConcurrencyMonteCarloPiFuture
CompletableFuture
commonPool
pp.08.03-CommonPool
CompletableFuture
Future
/Callable
Callable
wird Input für nächstes Callable
Callable
ausgeführt wird:
commonPool
CompletableFuture
Future
: read-only Container für zukünftiges Ergebnis (“Promise”)CompletionStage
: triggert weitere VerarbeitungCompletionStage
(Push-API)commonPool
Executor
commonPool
CompletionStage
CompletionStage
(Push-API)CompletableFuture
nach Hettel und Tran (2016, 240) (Änderungen: Annotationen und supplyAsync
statt applyAsync
in Start-API)
static CompletableFuture<Integer> calculateAsync() {
var result = new CompletableFuture<Integer>();
ForkJoinPool.commonPool().submit(() -> {
try {
var res = /* aufwändige Berechnung, Ergebnis z.B. */ 42;
result.complete(res);
} catch (Exception ex) {
result.completeExceptionally(ex);
}
});
return result;
}
public static void main(String... args) {
var cf = calculateAsync();
try {
System.out.println(cf.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
System.err.print(e);
}
}
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
public class SimpleCompletableFuture {
static class Task implements Supplier<Integer> {
@Override
public Integer get() {
return /* aufwändige Berechnung, Ergebnis z.B. */ 42;
}
}
public static void main(String... args)
throws InterruptedException, ExecutionException {
var future = CompletableFuture.supplyAsync(new Task());
System.out.println(future.get());
}
}
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class SimpleCompletableFuture {
public static void main(String... args)
throws InterruptedException, ExecutionException {
var future = CompletableFuture.supplyAsync(() -> {
return /* aufwändige Berechnung, Ergebnis z.B. */ 42;
});
System.out.println(future.get());
}
}
CompletableFuture
– lineare Ketten und Verzweigungen –
CompletableFuture<U> thenApplyAsync(Function<? super T, ? extends U> f)
CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
Für jedes thenApplyAsync
und theAcceptAsync
wird ggf. ein eigener Thread aus dem Threadpool benutzt.
Hettel und Tran (2016, 252) (modifiziert)
public class Service {
static User getUser(int userId) { ... }
static Profile getProfile(User user) { ... }
static AccessRight getAccessRight(Profile profile) { ... }
}
CompletableFuture<Void> cf = CompletableFuture
.supplyAsync(() -> Service.getUser(42))
.thenApplyAsync((user) -> Service.getProfile(user))
.thenApplyAsync((profile) -> Service.getAccessRight(profile))
.thenAcceptAsync((access) -> System.out.println(access));
cf.join(); // wartet auf das Ende der Berechnung von cf,
// hier also auf die erfolgte Ausgabe
public class Service {
public static User getUser(int userId) { ... }
public static Profile getProfile(User user) { ... }
public static AccessRight getAccessRight(Profile profile) { ... }
}
CompletableFuture<Void> cf = CompletableFuture
.supplyAsync(() -> T)
.thenApplyAsync((T t) -> U)
.thenApplyAsync((U u) -> V)
.thenAcceptAsync((V v) -> Void); // void (kleingeschrieben)
// ist in Java kein Typ, sondern ein Schlüsselwort in Signaturen
cf.join();
Methode | Task-Typ | Resultat | ||
---|---|---|---|---|
thenRun |
Runnable: () -> void |
CompletableFuture<Void> |
||
thenAccept |
Consumer: (T) -> void |
CompletableFuture<Void> |
||
thenApply |
Function: (T) -> U |
CompletableFuture<U> |
CompletableFuture
-Task schon fertig ist, wird der nächste Task im Aufrufer-Thread ausgeführt.CompletableFuture
-Task noch nicht fertig ist, wird der nächste Task anschließend im Thread des vorigen Tasks ausgeführt.var task1 = CompletableFuture.supplyAsync(() -> "4711");
var task2 = task1.thenApplyAsync((in) -> in.length());
var task3 = task1.thenApplyAsync((in) -> in.equals("42"));
T
: String
'4711'
U
: Integer
4
V
: Boolean
false
Hettel und Tran (2016, 252)
Hettel und Tran (2016, 252)
Methoden zur Vereinigung von Abläufen. “CF” steht für CompletableFuture
und “CS” für CompletionStage
thenCombine
(Verrechnen)var task1 = CompletableFuture.supplyAsync(() -> 47);
var task2 = CompletableFuture.supplyAsync(() -> "11");
var task3 = task1.thenCombineAsync(task2, (n, s)->String.valueOf(n)+s);
T
: Integer
47
U
: String
'11'
V
: String
'4711'
Hettel und Tran (2016, 253)
applyToEither
(“ODER”)var task1 = CompletableFuture.supplyAsync(() -> 47);
var task2 = CompletableFuture.supplyAsync(() -> 11);
var task3 = task1.applyToEitherAsync(task2, (n) -> n > 20);
T
: Integer
47
U
: Integer
11
V
: Boolean
true
oder false
Hettel und Tran (2016, 255)
Hettel und Tran (2016, 257) (modifiziert)
CompletableFuture.allOf( // alle müssen beendet werden
CompletableFuture.runAsync( () -> { /*...*/ } ),
CompletableFuture.runAsync( () -> { /*...*/ } ),
CompletableFuture.runAsync( () -> { /*...*/ } )
).thenAccept((Void) -> System.out.println("done") );
CompletableFuture.anyOf( // das frühest fertige
CompletableFuture.supplyAsync( () -> { /*...*/ } ),
CompletableFuture.supplyAsync( () -> { /*...*/ } ),
CompletableFuture.supplyAsync( () -> { /*...*/ } )
).thenAccept((first) -> System.out.println(first));
var cf = CompletableFuture
.supplyAsync(() -> 42)
.thenApplyAsync(r -> r / 0)
.thenApplyAsync(r -> r * r)
.thenApplyAsync(r -> r > 0)
.handle((r, th) -> {
if (r != null) {
System.out.println("Resultat: " + r);
return r;
} else {
System.err.println("error: " + th);
return false;
}
});
System.out.println(cf.join());
Hettel und Tran (2016, 258) (modifiziert)
var cf = CompletableFuture
.supplyAsync(() -> 42)
.thenApplyAsync(r -> r / 0)
.thenApplyAsync(r -> r * r)
.thenApplyAsync(r -> r > 0)
.whenComplete((r, th) -> {
if (r == null) {
System.err.println("error: " + th);
}
});
cf.join();
Hettel und Tran (2016, 258) (modifiziert)
CompletableFuture
”pp.08.04-ConcurrencyMonteCarloPiCF