overview of advanced java 8 completablefuture features ...schmidt/cs891f/2018-pdfs/... · 2...
TRANSCRIPT
Overview of Advanced Java 8
CompletableFuture Features (Part 2)
Douglas C. [email protected]
www.dre.vanderbilt.edu/~schmidt
Professor of Computer Science
Institute for Software
Integrated Systems
Vanderbilt University
Nashville, Tennessee, USA
2
Learning Objectives in this Part of the Lesson• Understand advanced features
of completable futures, e.g.
• Factory methods that initiateasync functionality
• Completion stage methods usedto chain together actions that perform async result processing& composition
Exception methods
Completion stage methods
Factory methodsArbitrary-arity
methods
Basic methods
3
Completion Stage Methods Chain Actions Together
4See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html
• A completable future can serve as a ”completion stage” for async result processing
Completion Stage Methods Chain Actions Together
5
• A completable future can serve as a ”completion stage” for async result processing
• An action is performed on a completed async call result
Completion Stage Methods Chain Actions TogetherBigFraction unreduced = BigFraction
.valueOf(new BigInteger
("846122553600669882"),
new BigInteger
("188027234133482196"),
false); // Don’t reduce!
Supplier<BigFraction> reduce = () ->
BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
...
thenApply()’s action is triggered when future from supplyAsync() completes
6
• A completable future can serve as a ”completion stage” for async result processing
• An action is performed on a completed async call result
• Methods can be chainedtogether “fluently”
Completion Stage Methods Chain Actions TogetherBigFraction unreduced = BigFraction
.valueOf(new BigInteger
("846122553600669882"),
new BigInteger
("188027234133482196"),
false); // Don’t reduce!
Supplier<BigFraction> reduce = () ->
BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
thenAccept()’s action is triggered when future from
thenApply() completes
See en.wikipedia.org/wiki/Fluent_interface
7
• A completable future can serve as a ”completion stage” for async result processing
• An action is performed on a completed async call result
• Methods can be chainedtogether “fluently”
• Each method registers a lambda action to apply
Completion Stage Methods Chain Actions TogetherBigFraction unreduced = BigFraction
.valueOf(new BigInteger
("846122553600669882"),
new BigInteger
("188027234133482196"),
false); // Don’t reduce!
Supplier<BigFraction> reduce = () ->
BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
8
• A completable future can serve as a ”completion stage” for async result processing
• An action is performed on a completed async call result
• Methods can be chainedtogether “fluently”
• Each method registers a lambda action to apply
• A lambda action is called only after previous stage completes successfully
Completion Stage Methods Chain Actions TogetherBigFraction unreduced = BigFraction
.valueOf(new BigInteger
("846122553600669882"),
new BigInteger
("188027234133482196"),
false); // Don’t reduce!
Supplier<BigFraction> reduce = () ->
BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
9
• A completable future can serve as a ”completion stage” for async result processing
• An action is performed on a completed async call result
• Methods can be chainedtogether “fluently”
• Each method registers a lambda action to apply
• A lambda action is called only after previous stage completes successfully
Completion Stage Methods Chain Actions TogetherBigFraction unreduced = BigFraction
.valueOf(new BigInteger
("846122553600669882"),
new BigInteger
("188027234133482196"),
false); // Don’t reduce!
Supplier<BigFraction> reduce = () ->
BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
Action is “deferred” until previous stage completes & fork-join thread is available
10
• Use completion stages to avoid blocking a thread until the result must be obtained
Completion Stage Methods Chain Actions Together
11
• Use completion stages to avoid blocking a thread until the result must be obtained, e.g.
• Try not to call join() or get()unless absolutely necessary
Completion Stage Methods Chain Actions Together
Servers may avoid blocking completely, whereas clients may need join() sparingly
Join Us
12
• Use completion stages to avoid blocking a thread until the result must be obtained, e.g.
• Try not to call join() or get()unless absolutely necessary
• This approach helps improveresponsiveness
Completion Stage Methods Chain Actions Together
13
• A completable future can serve as a ”completion stage” for async result processing
Completion Stage Methods Chain Actions Together
Juggling is a good analogy for completion stages!
14
Grouping CompletableFutureCompletion Stage Methods
15
• Completion stage methods are grouped based on howa stage is triggered by oneor more previous stage(s)
See www.jesperdj.com/2015/09/26/the-future-is-completable-in-java-8
Exception methods
Completion stage methods
Factory methodsArbitrary-arity
methods
Basic methods
Grouping CompletableFuture Completion Stage Methods
16
• Completion stage methods are grouped based on howa stage is triggered by oneor more previous stage(s)
• Completion of a single previous stage
Methods Params Returns Behavior
thenApply(Async)
Function CompletableFuture with Function result
Apply function to result of the previous stage
thenCompose(Async)
Function CompletableFuture with Function result directly, not a nested future
Apply function to result of the previous stage
thenAccept(Async)
Consumer CompletableFuture<Void>
Consumer handles result of previous stage
thenRun(Async)
Runnable CompletableFuture<Void>
Run action w/out returning value
Grouping CompletableFuture Completion Stage Methods
These methods run in the invoking thread or the same
thread as previous stage
The thread that executes these methods depends on various runtime factors
17
Methods Params Returns Behavior
thenApply(Async)
Function CompletableFuture with Function result
Apply function to result of the previous stage
thenCompose(Async)
Function CompletableFuture with Function result directly, not a nested future
Apply function to result of the previous stage
thenAccept(Async)
Consumer CompletableFuture<Void>
Consumer handles result of previous stage
thenRun(Async)
Runnable CompletableFuture<Void>
Run action w/out returning value
See blog.krecan.net/2013/12/25/completablefutures-why-to-use-async-methods
• Completion stage methods are grouped based on howa stage is triggered by oneor more previous stage(s)
• Completion of a single previous stage
Grouping CompletableFuture Completion Stage Methods
*Async() variants run in common fork-join pool
18
• Completion stage methods are grouped based on howa stage is triggered by oneor more previous stage(s)
• Completion of a single previous stage
• Completion of both of 2 previous stages
• i.e., an “and”
Methods Params Returns Behavior
thenCombine(Async)
BiFunction
CompletableFuture with BiFunction result
Apply bifunctionto results of both previous stages
thenAcceptBoth
(Async)
BiConsumer
CompletableFuture<Void>
BiConsumerhandles results of both previous stages
runAfterBoth
(Async)
Runnable CompletableFuture<Void>
Run action when both previous stages complete
Grouping CompletableFuture Completion Stage Methods
19
• Completion stage methods are grouped based on howa stage is triggered by oneor more previous stage(s)
• Completion of a single previous stage
• Completion of both of 2 previous stages
• Completion of either of 2 previous stages
• i.e., an “or”
Methods Params Returns Behavior
applyToEither
(Async)
Function CompletableFuture with Function result
Apply function to results of either previous stage
acceptEither
(Async)
Consumer CompletableFuture<Void>
Consumer handles results of either previous stage
runAfterEither
(Async)
Runnable CompletableFuture<Void>
Run action when either previous stage completes
Grouping CompletableFuture Completion Stage Methods
20
Key CompletableFutureCompletion Stage Methods
21
• Methods triggered by completion of a single previous stage
• thenApply()
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenApply
(Function<? super T,
? extends U> fn)
{ ... }
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenApply
22
• Methods triggered by completion of a single previous stage
• thenApply()
• Applies a function action to the previous stage’s result
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenApply
(Function<? super T,
? extends U> fn)
{ ... }
23
• Methods triggered by completion of a single previous stage
• thenApply()
• Applies a function action to the previous stage’s result
• Returns a future containingthe result of the action
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenApply
(Function<? super T,
? extends U> fn)
{ ... }
24
• Methods triggered by completion of a single previous stage
• thenApply()
• Applies a function action to the previous stage’s result
• Returns a future containingthe result of the action
• Used for a sync action that returns a value, not a future
Key CompletableFuture Completion Stage MethodsBigFraction unreduced = BigFraction
.valueOf(new BigInteger("..."),
new BigInteger("..."),
false); // Don’t reduce!
Supplier<BigFraction> reduce = ()
-> BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
...
e.g., toMixedString() returns a string value
25
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCompose
(Function<? super T,
? extends
CompletionStage<U>> fn)
{ ... }
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenCompose
26
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCompose
(Function<? super T,
? extends
CompletionStage<U>> fn)
{ ... }
27
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• i.e., not a nested future
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCompose
(Function<? super T,
? extends
CompletionStage<U>> fn)
{ ... }
28
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• i.e., not a nested future
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCompose
(Function<? super T,
? extends
CompletionStage<U>> fn)
{ ... }
See dzone.com/articles/understanding-flatmap
+ =
≠+thenCompose() is similar to flatMap() on a Stream or Optional
29
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
Key CompletableFuture Completion Stage Methods
e.g., supplyAsync() returns a completable future
Function<BF,
CompletableFuture<BF>>
reduceAndMultiplyFractions =
unreduced -> CompletableFuture
.supplyAsync
(() -> BF.reduce(unreduced))
.thenCompose
(reduced -> CompletableFuture
.supplyAsync(() ->
reduced.multiply(...)));
...
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#supplyAsync
30
Function<BF, CompletableFuture<
CompletableFuture<BF>>>
reduceAndMultiplyFractions =
unreduced -> CompletableFuture
.supplyAsync
(() -> BF.reduce(unreduced))
.thenApply
(reduced -> CompletableFuture
.supplyAsync(() ->
reduced.multiply(...)));
...
...
Unwieldy!
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
• Avoids unwieldy nesting offutures à la thenApply()
Key CompletableFuture Completion Stage Methods
31
Function<BF,
CompletableFuture<BF>>
reduceAndMultiplyFractions =
unreduced -> CompletableFuture
.supplyAsync
(() -> BF.reduce(unreduced))
.thenApplyAsync(reduced
-> reduced.multiply(...)));
...
...
Concise!
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
• Avoids unwieldy nesting offutures à la thenApply()
Key CompletableFuture Completion Stage Methods
thenApplyAsync() can often replace thenCompose(supplyAsync()) nestings
32
supplyAsync() will return a CompletableFuture to a
CompletableFuture here!!
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
• Avoids unwieldy nesting offutures à la thenApply()
Key CompletableFuture Completion Stage Methods
Can be used to avoid calling join() when flattening nested completable futures
CompletableFuture<Integer> countF =
.CompletableFuture
.supplyAsync
(() ->
longRunnerReturnsCF())
.thenCompose
(Function.identity())
...
33
This idiom flattens the return value to “just” one CompletableFuture!
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
• Avoids unwieldy nesting offutures à la thenApply()
Key CompletableFuture Completion Stage Methods
Can be used to avoid calling join() when flattening nested completable futures
CompletableFuture<Integer> countF =
.CompletableFuture
.supplyAsync
(() ->
longRunnerReturnsCF())
.thenCompose
(Function.identity())
...
34
Runs longBlockerReturnsCF() in a thread in the fork-join pool
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• Applies a function action to the previous stage’s result
• Returns a future containingresult of the action directly
• Used for an async action that returns a completable future
• Avoids unwieldy nesting offutures à la thenApply()
Key CompletableFuture Completion Stage Methods
thenComposeAsync() can be used to avoid calling supplyAsync() again in a chain
CompletableFuture<Integer> countF =
.CompletableFuture
.supplyAsync
(() ->
longRunnerReturnsCF())
.thenComposeAsync
(this::longBlockerReturnsCF)
...
35
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void>
thenAccept
(Consumer<? super T> action)
{ ... }
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenAccept
36
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
• Applies a consumer action to handle previous stage’s result
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void>
thenAccept
(Consumer<? super T> action)
{ ... }
This action behaves as a “callback” with a side-effect
See en.wikipedia.org/wiki/Callback_(computer_programming)
37
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
• Applies a consumer action to handle previous stage’s result
• Returns a future to Void
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void>
thenAccept
(Consumer<? super T> action)
{ ... }
38
BigFraction unreduced = BigFraction
.valueOf(new BigInteger("..."),
new BigInteger("..."),
false); // Don’t reduce!
Supplier<BigFraction> reduce = ()
-> BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
• Applies a consumer action to handle previous stage’s result
• Returns a future to Void
• Often used at the end of a chain of completion stages
Key CompletableFuture Completion Stage Methods
thenApply() returns a string future that thenAccept() prints when it completes
39
BigFraction unreduced = BigFraction
.valueOf(new BigInteger("..."),
new BigInteger("..."),
false); // Don’t reduce!
Supplier<BigFraction> reduce = ()
-> BigFraction.reduce(unreduced);
CompletableFuture
.supplyAsync(reduce)
.thenApply(BigFraction
::toMixedString)
.thenAccept(System.out::println);
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
• Applies a consumer action to handle previous stage’s result
• Returns a future to Void
• Often used at the end of a chain of completion stages
Key CompletableFuture Completion Stage Methods
println() is a callback that has a side-effect (i.e., printing the mixed string)
40
• Methods triggered by completion of a single previous stage
• thenApply()
• thenCompose()
• thenAccept()
• Applies a consumer action to handle previous stage’s result
• Returns a future to Void
• Often used at the end of a chain of completion stages
• May lead to “callback hell!”
Key CompletableFuture Completion Stage Methods
See dzone.com/articles/callback-hell
41
• Methods triggered by completion of both of two previous stages
• thenCombine()
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCombine
(CompletionStage<? Extends U>
other,
BiFunction<? super T,
? super U,
? extends V> fn)
{ ... }
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenCombine
42
• Methods triggered by completion of both of two previous stages
• thenCombine()
• Applies a bifunction action to two previous stages’ results
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCombine
(CompletionStage<? Extends U>
other,
BiFunction<? super T,
? super U,
? extends V> fn)
{ ... }
43
• Methods triggered by completion of both of two previous stages
• thenCombine()
• Applies a bifunction action to two previous stages’ results
• Returns a future containingthe result of the action
Key CompletableFuture Completion Stage MethodsCompletableFuture<U> thenCombine
(CompletionStage<? Extends U>
other,
BiFunction<? super T,
? super U,
? extends V> fn)
{ ... }
44
• Methods triggered by completion of both of two previous stages
• thenCombine()
• Applies a bifunction action to two previous stages’ results
• Returns a future containingthe result of the action
Key CompletableFuture Completion Stage Methods
thenCombine() essentially performs a “reduction”
CompletableFuture<U> thenCombine
(CompletionStage<? Extends U>
other,
BiFunction<? super T,
? super U,
? extends V> fn)
{ ... }
45
• Methods triggered by completion of both of two previous stages
• thenCombine()
• Applies a bifunction action to two previous stages’ results
• Returns a future containingthe result of the action
• Used to “join” two pathsof asynchronous execution
Key CompletableFuture Completion Stage MethodsCompletableFuture<BF> compF1 =
CompletableFuture
.supplyAsync(() ->
/* multiply two BFs. */);
CompletableFuture<BF> compF2 =
CompletableFuture
.supplyAsync(() ->
/* divide two BFs. */);
compF1
.thenCombine(compF2,
BigFraction::add)
.thenAccept(System.out::println);
thenCombine()’s action is triggered when its two
associated futures complete
46
• Methods triggered by completion of either of two previous stages
• acceptEither()
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void> acceptEither
(CompletionStage<? Extends T>
other,
Consumer<? super T> action)
{ ... }
See docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#acceptEither
47
• Methods triggered by completion of either of two previous stages
• acceptEither()
• Applies a consumer actionthat handles either of theprevious stages' results
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void> acceptEither
(CompletionStage<? Extends T>
other,
Consumer<? super T> action)
{ ... }
48
• Methods triggered by completion of either of two previous stages
• acceptEither()
• Applies a consumer actionthat handles either of theprevious stages' results
• Returns a future to Void
Key CompletableFuture Completion Stage MethodsCompletableFuture<Void> acceptEither
(CompletionStage<? Extends T>
other,
Consumer<? super T> action)
{ ... }
49
• Methods triggered by completion of either of two previous stages
• acceptEither()
• Applies a consumer actionthat handles either of theprevious stages' results
• Returns a future to Void
• Often used at the end of achain of completion stages
Key CompletableFuture Completion Stage MethodsCompletableFuture<List<BigFraction>>
quickSortF = CompletableFuture
.supplyAsync(() ->
quickSort(list));
CompletableFuture<List<BigFraction>>
mergeSortF = CompletableFuture
.supplyAsync(() ->
mergeSort(list));
Create two completable futures that will contain the results of
sorting the list using two different algorithms in two different threads
50
• Methods triggered by completion of either of two previous stages
• acceptEither()
• Applies a consumer actionthat handles either of theprevious stages' results
• Returns a future to Void
• Often used at the end of achain of completion stages
Key CompletableFuture Completion Stage MethodsCompletableFuture<List<BigFraction>>
quickSortF = CompletableFuture
.supplyAsync(() ->
quickSort(list));
CompletableFuture<List<BigFraction>>
mergeSortF = CompletableFuture
.supplyAsync(() ->
mergeSort(list));
quickSortF.acceptEither
(mergeSortF, results -> results
.forEach(fraction ->
System.out.println
(fraction
.toMixedString())));
Printout sorted results from which ever sorting routine finished first
acceptEither() does not cancel the second future after the first one completes
51
End of Overview of Advanced Java 8 Completable Future
Features (Part 2)