Slides used at DevNexus 22 talk.
Completable futures were introduced in Java 8, and they are a powerful mechanism to add concurrency (not parallelism) to your application logic, and it may be handy when dealing with multiple queries to different systems.
If you are a developer, you may make your code more performant by using CompletableFutures where it makes sense.
If you are a library developer, it may be a good thing to offer an API that returns CompletableFutures so that your users will be able to benefit from it.
3. Same type of tasks
Workload split between similar workers
Have the work finished before
Parallelism Concurrency
Different type of tasks
Usually do some works while waiting for
other tasks to be completed.
Have the work finished before
@DGOMEZG
5. Lots of tools since Java 1
Java has (basic) building blocks for Concurrency and parallelism
since Java 1.
Even when most desktop computers were single-core
@DGOMEZG
12. Since Java 5
Concurrency & parallelism support improved in Java 5
Lots of New Concurrency constructors
New concurrent-friendly Data Structures
Revisited and improved in Java 7, 8, 9 …
More coming up as part of loom project
@DGOMEZG
14. ExecutorService
And now we can submit:
- tasks that do not return anything. Runnable
- tasks that return a result. Callable<R>
Executor Service returns a Future<R>
@DGOMEZG
16. ExecutorService & Future<T>
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> taskResult =
executorService.submit(longRunningTaskWithResult);
while (!taskResult.isDone()) {
doSomethingElse();
}
String s = taskResult.get();
False while task is not cancelled
nor
fi
nished
Improving performance by doing
other tasks.
17. Running Multiple T
asks
List<Future<String
>
>
taskResults = new ArrayList
<
>
();
for (Callable<String> task : tasks) {
taskResults.add(executorService.submit(task));
}
for (Future<String> result : taskResults) {
processResult(result.get());
}
Bene
fi
t only between submitting
all tasks and getting the results
18. Running Heterogeneous T
asks
List<Future<String
>
>
taskResults = new ArrayList
<
>
();
for (Callable<String> task : tasks) {
taskResults.add(executorService.submit(task));
}
for (Future<String> result : taskResults) {
processResult(result.get());
}
What if task1 is too slow
while task2 is fast?
What if we need the result of
task1 to submit task2?
(CompletionService returns
tasks in the order they are
fi
nished)
20. CompletableFuture<T>
Introduced in Java8
Has specific methods to submit tasks to an ExecutorService
Implements a fluent API (CompletionStage) that allows chaining and
combining dependant tasks.
Allow to use different sized ExecutorServices for different tasks.
@DGOMEZG
23. Async with CompletableFuture
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult());
doSomethingElse();
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult());
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
24. Async with CompletableFuture
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult());
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
private static final Executor ASYNC_POOL = USE_COMMON_POOL ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
25. Async with CompletableFuture
private static final Executor ASYNC_POOL = USE_COMMON_POOL ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
Shared with the JVM (may affect parallel
streams)
Sized for CPU intensive tasks (if our task is
IO bound, we may affect app performance)
Daemon Threads (if our main thread
finishes, tasks may not be executed)
Same that
new Thread(task).start();
26. Async with CompletableFuture
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult());
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
private static final Executor ASYNC_POOL = USE_COMMON_POOL ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
27. Async with CompletableFuture
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult());
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
28. Async with CompletableFuture
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
Sized and used for the expected % of CPU used by
your tasks
29. Async with CompletableFuture
CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
30. CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
NoResult()
Async with CompletableFuture
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
31. CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
NoResult()
Async with CompletableFuture
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
Result()
32. CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
voidCF.get();
/
/
Wait until background task is finished
Result()
Async with CompletableFuture
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
35. CompletableFuture<Void> voidCF =
CompletableFuture.runAsync(getLongRunningTaskWithNoResult()
, );
doSomethingElse();
result.get();
/
/
Wait until background task is finished
String> result =
sup Async(getLongRunningTaskWithResult()
Async with CompletableFuture
ExecutorService executorService = Executors.newCachedThreadPool();
executorService
ply
36. CompletableFuture<T>
It may seem just an alternative to Future<T>
public class CompletableFuture<T>
implements Future<T>, CompletionStage<T>
CompletionStage<T>
@DGOMEZG
38. Chaining tasks (blocking)
void invokeSlowProcess() {
Order order = orderService.getOrder();
customerChecker.authorized(order.customer());
}
@DGOMEZG
executed by main in PT7.045928S
invokeSlowServiceDependantTasks();
39. Chaining tasks with CompletableFuture
@DGOMEZG
CompletableFuture
.supplyAsync(
()
-
>
orderService.getOrder()
).thenAcceptAsync(
order
-
>
customerChecker.authorized(order.customer())
);
Submitted by main in PT0.058982S
Executed by ForkJoinPool.commonPool-worker-1 in PT2.008558S
Executed by ForkJoinPool.commonPool-worker-2 in PT5.010714S
41. Chaining tasks
var orderCF = CompletableFuture.supplyAsync(
()
-
>
orderService.getOrder()
);
orderCF.thenAcceptAsync(
order
-
>
activityTracker.orderAccessed(order)));
orderCF.thenAcceptAsync(
order
-
>
customerChecker.authorized(order.customer())));
@DGOMEZG
Submitted by main in PT0.071653S
getOrder executed by ForkJoinPool.commonPool-worker-1 in PT2.006599S
activityTracker executed by ForkJoinPool.commonPool-worker-3 in PT2.009098S
customerChecker executed by ForkJoinPool.commonPool-worker-2 in PT5.007562S
46. Why should you consider using CompletableFuture
in you API?
Enable your users to maximise the performance to your queries.
And keep control on how you execute the asynchronous tasks.
Benefit from how other frameworks handle CompletableFutures
(i. e Spring)
@DGOMEZG
47. Creating and managing CompletableFutures
CompletableFuture<String> completableFuture =
new CompletableFuture
<
>
();
@DGOMEZG
53. QueryBusImpl (simplified)
public CompletableFuture<QueryResponseMessage> query(QueryMessage query) {
/
/
Create your CompletableFuture
CompletableFuture<QueryResponseMessage> queryTransaction =
new CompletableFuture
<
>
();
/
/
Prepare to Run asynchronously
/
/
get Results/Exceptions to complete the future
/
/
return your completableFuture
return queryTransaction;
}
54. QueryBusImpl (simplified)
public CompletableFuture<QueryResponseMessage> query(QueryMessage query) {
CompletableFuture<QueryResponseMessage> queryTransaction =
new CompletableFuture
<
>
();
try {
/
/
Prepare to Run asynchronously
/
/
get Results to complete the future
} catch (Exception e) {
queryTransaction.completeExceptionally(exception);
}
return queryTransaction;
}
56. Benefits
It allows to compose asynchronous query calls to different systems
@DGOMEZG
private final QueryGateway queryGateway;
@GetMapping("orders/{OrderId}/status")
public Future<OrderStatus> orderStatus(@PathVariable String orderId) {
CompletableFuture<OrderStatus> orderStatusCF =
queryGateway.query(new OrderStatusQuery(orderId),
new InstanceResponseType
<
>
(OrderStatus.class));
/
/
Invoke other external services asynchronously
/
/
Composing the tasks with CompletionStage.*
/
/
Useful in processes when applying the "Strangling The monolith Pattern”
/
/
Probably not very usual when using DDD
return orderStatusCF;
}
57. Benefits
SpringFramework knows how to return a Future<T>
Tomcat (and most application Servers) can handle better the Threads in the ThreadPool
@DGOMEZG
private final QueryGateway queryGateway;
@GetMapping(“orders/{OrderId}/status")
public Future<OrderStatus> orderStatus(@PathVariable String orderId) {
return CompletableFuture<OrderStatus> orderStatusCF =
queryGateway.query(new OrderStatusQuery(orderId),
new InstanceResponseType
<
>
(OrderStatus.class));
}
59. Conclusions
CompletableFutures are a powerful tool to compose and combine
Asynchronous execution of heterogeneous tasks
Best if used with specific ExecutorService instances, properly sized based
depending on amount of I/O-Wait time
Adding CompletableFutures on you Async API will give more powers to the
developers using your library/framework.
@DGOMEZG
60. Where do I go from here?
https://github.com/AxonFramework/AxonFramework
Quick Start Tutorial.
https://docs.axoniq.io/reference-guide/getting-started/quick-start
Free Courses on DDD, CQRS, Event-Sourcing
https://academy.axoniq.io/
https:/
/lp.axoniq.io/devnexus-22
The slides
JavaSpecialists newsletter
Java Concurrency Specialist Courses