Photo by Silas Baisch on Unsplash

Asynchronous programming in Java with CompletableFuture

Introduction

Futures and promises

CompletableFuture in practice

logger.info("this task started");int netAmountInUsd = getPriceInEur() * getExchangeRateEurToUsd(); // blocking
float tax = getTax(netAmountInUsd); // blocking
float grossAmountInUsd = netAmountInUsd * (1 + tax);
logger.info("this task finished: {}", grossAmountInUsd);
logger.info("another task started");
logger.info("this task started");Future<Integer> priceInEur = executorService.submit(this::getPriceInEur);
Future<Integer> exchangeRateEurToUsd = executorService.submit(this::getExchangeRateEurToUsd);
while (!priceInEur.isDone() || !exchangeRateEurToUsd.isDone()) { // non-blocking
Thread.sleep(100);
logger.info("another task is running");
}
int netAmountInUsd = priceInEur.get() * exchangeRateEurToUsd.get(); // actually non-blocking
Future<Float> tax = executorService.submit(() -> getTax(netAmountInUsd));
while (!tax.isDone()) { // non-blocking
Thread.sleep(100);
logger.info("another task is running");
}
float grossAmountInUsd = netAmountInUsd * (1 + tax.get()); // actually non-blockinglogger.info("this task finished: {}", grossAmountInUsd);
logger.info("another task is running");
CompletableFuture<Integer> priceInEur = CompletableFuture.supplyAsync(this::getPriceInEur);
CompletableFuture<Integer> exchangeRateEurToUsd = CompletableFuture.supplyAsync(this::getExchangeRateEurToUsd);
CompletableFuture<Integer> netAmountInUsd = priceInEur
.thenCombine(exchangeRateEurToUsd, (price, exchangeRate) -> price * exchangeRate);
logger.info("this task started");netAmountInUsd
.thenCompose(amount -> CompletableFuture.supplyAsync(() -> amount * (1 + getTax(amount))))
.whenComplete((grossAmountInUsd, throwable) -> {
if (throwable == null) {
logger.info("this task finished: {}", grossAmountInUsd);
} else {
logger.warn("this task failed: {}", throwable.getMessage());
}
}); // non-blocking
logger.info("another task started");

The CompletionStage interface

Methods to pipeline computations

CompletableFuture<Double> pi = CompletableFuture.supplyAsync(() -> Math.PI);
CompletableFuture<Integer> radius = CompletableFuture.supplyAsync(() -> 1);
// area of a circle = π * r^2
CompletableFuture<Void> area = radius
.thenApply(r -> r * r)
.thenCombine(pi, (multiplier1, multiplier2) -> multiplier1 * multiplier2)
.thenAccept(a -> logger.info("area: {}", a))
.thenRun(() -> logger.info("operation completed"));
area.join();

Methods to handle exceptions

CompletableFuture.supplyAsync(() -> 0)
.thenApply(i -> { logger.info("stage 1: {}", i); return 1 / i; }) // executed and failed
.thenApply(i -> { logger.info("stage 2: {}", i); return 1 / i; }) // skipped
.whenComplete((value, t) -> {
if (t == null) {
logger.info("success: {}", value);
} else {
logger.warn("failure: {}", t.getMessage()); // executed
}
})
.thenApply(i -> { logger.info("stage 3: {}", i); return 1 / i; }) // skipped
.handle((value, t) -> {
if (t == null) {
return value + 1;
} else {
return -1; // executed and recovered
}
})
.thenApply(i -> { logger.info("stage 4: {}", i); return 1 / i; }) // executed
.join();

The CompletableFuture class

ExecutorService executorService = Executors.newSingleThreadExecutor();CompletableFuture<String> future = new CompletableFuture<>(); // creating an incomplete futureexecutorService.submit(() -> {
Thread.sleep(500);
future.complete("value"); // completing the incomplete future
return null;
});
while (!future.isDone()) { // checking the future for completion
Thread.sleep(1000);
}
String result = future.get(); // reading value of the completed future
logger.info("result: {}", result);
executorService.shutdown();

Methods to create futures

Methods to check futures

Methods to complete futures

Methods to read futures

Methods for bulk future operations

Conclusion

Senior Software Engineer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store