JavaScript Required

We're sorry, but we doesn't work properly without JavaScript enabled.

Looking for an Expert Development Team? Take two weeks Trial! Try Now

Java 8 CompletableFuture

Technology: CompletableFuture is the new feature added in java 8, which is an extension of Java Future API which was introduced in Java5.

CompletableFuture is used for asynchronous programming in Java. Asynchronous Programming means running task in a separate thread, other than main thread, and notifying the execution progress like completion or failure.

It helps in improving the performance of the application, as it is executing separately from main thread.

Comparison between Future and CompletableFuture:

CompletableFuture is an extension to Java Future API. Future is the return type of the method of an asynchronous execution. Future provides isDone()to check the completion status of task, and get() method is used to retrieve the output/ return value of the execution.

Future is the first implementation to support the Asynchronous Programming in Java, and it comes with some limitations and some useful features.

Limitations of Future API:

1. Manual Completion method:

suppose if the requested some Rest API, and it is taking too long time to receive the message. We can not set the cached response and mark the task is done.

2. Notification to main execution thread

Future will not notify the main execution thread after it completes the execution. It provides get() method, which will block the main thread until it completes and provides the response. Future API doesn`t provide any callback methods which will be called upon success/failure of the task completion status.

3. Multiple Futures cannotchain together:

sometimes one task may depend on another task result, in these cases we need to chain these tasks based on previous task completion status, these are not provided in Future API.

4. Combining Multiple Future Together

let’s say we are executing 10 tasks in parallel using Future API, and we want to execute some piece of code once all these tasks are completed, this feature is not provided in Future.

5. Exception Handling:

Future API does not provide any exception handling.

All these limitations are implemented by extending the Future in CompletableFuture.This Class implements both Future, CompletionStage interface.

CompletionStage interface:

A stage of a possibly asynchronous computation, that performs an action or computes a value when another CompletionStage completes. A stage completes upon termination of its computation, but this may in turn trigger other dependent stages. The functionality defined in this interface takes only a few basic forms, which expand out to a larger set of methods to capture a range of usage styles.

All methods adhere to the triggering, execution, and exceptional completion specifications Additionally, while arguments used to pass a completion result (that is, for parameters of type T) for methods accepting them may be null, passing a null value for any other parameter will result in a NullPointerException being thrown.

1. Basic Example to create CompletableFuture:

CompletableFuture<String> future = new CompletableFuture<String>(); future.complete("result!!"); String result = future.get();

future is the object to execute the empty task, and we marking task as complete with specified string, later we are invoking get() method to get return of the task. Any subsequent calls to future object will be ignored as the task was completed.

2. Running Tasks in asynchronous way using runAsync():

CompletableFuture<Void>completableFuture = CompletableFuture.runAsync(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("executing using runAsync() method"); } }); completableFuture.get(); the same can be written as below using lambdas: CompletableFuture<Void>completableFuture = CompletableFuture.runAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("executing using runAsync() method"); }); completableFuture.get(); runAsync() method takes Runnable implementation and return void type of CompletableFuture.

3. If the CompletableFuture needs to inform the main thread with some return value then we can use supplyAsync() method.

CompletableFuture<String>completableFuture = CompletableFuture.supplyAsync(() - > { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "executing using supplyAsync() method"; }); String returnValue = completableFuture.get(); System.out.println(returnValue);

Chaining CompletableFutures :

  1. thenApply() : thenApply() is the method useful to execute some function after returning the completableFuture. Its like callback for CompletableFuture, this callback function will be executed by main thread after CompletableFuture return the value to main thread.
CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "sravan"; }); CompletableFuture<String>fullName = firstName.thenApply(fName ->fName+" Kumar"); System.out.println(fullName.get());
  1. thenApplyAsync(): this method will do same like thenApply() method, but the difference is callback will be executed in separate thread, where as thenApply() will be executed by main thread.
CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "sravan"; }); CompletableFuture<String>fullName = firstName.thenApplyAsync(fName ->fName+" Kumar"); System.out.println(fullName.get()); We can chain thenApply() methods to any number, return value of the previous thenApply() method value is input next thenApply() method and it goes..
  1. thenAccept(): if the callback functions don’t want to return anything then thenAccept() and thenRun() methods will be used.

The difference between these are thenAccept() will take value from previous execution, but thenRun() will not depend on the previous execution.

CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "sravan"; }); CompletableFuture<Void>fullName = firstName.thenAccept(fName -> { System.out.println(fName +" Kumar"); }); fullName.get(); same method signature is available for Async style also.
  1. Chaining CompletableFutures: Let`s say that we have 2 applications, for one provides the user information, other will provide the user credit card information. If we want to fetch the user credit card information in parallelly, then we can use thenApply() method, like:
CompletableFuture<User>getUsersDetail(String userId) { return CompletableFuture.supplyAsync(() -> { UserService.getUserDetails(userId); });         } CompletableFuture<Double>getCreditRating(User user) { return CompletableFuture.supplyAsync(() -> { CreditRatingService.getCreditRating(user); }); } CompletableFuture<CompletableFuture<Double>> result = getUserDetail(userId).thenApply(user ->getCreditRating(user)); But the return type CompletableFuture of CompletableFuture. In these cases CompletabelFuture is providing thenCompose() method for returning CompletableFuture of Object type. CompletableFuture<Double> result = getUserDetail(userId).thenCompose(user ->getCreditRating(user));

4. thenCombine(): we can combine two independent CompletableFutures with thenCombine() method.

CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "Sravan"; }); CompletableFuture<String>lastName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "Kumar"; }); CompletableFuture<String>fullName = firstName.thenCombine(lastName, (fName, lName) ->fName + " " + lName); System.out.println("fullName - " + fullName.get());

5. applyToEither() : lets we want to execute the some method for first resulted between two CompletableFuture then we can use this method.

CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "Sravan"; }); CompletableFuture<String>lastName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return "Kumar"; }); CompletableFuture<String>fullName = firstName.applyToEither(lastName, name -> name); System.out.println("first returned  - " + fullName.get());

6. acceptEither(): it is same as applyToEither() but it will not return any value.

CompletableFuture<String>firstName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "Sravan"; }); CompletableFuture<String>lastName = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return "Kumar"; }); CompletableFuture<Void>fullName = firstName.acceptEither(lastName, name -> { System.out.println(name); });

Exception Handling in CompletableFuture:

Similar thenApply() successful callback, we have one more callback for exception, exceptionally() method.

Integer age = -1; CompletableFuture<String>maturityFuture = CompletableFuture.supplyAsync(() -> { if (age < 0) { throw new IllegalArgumentException("Age can not be negative"); } if (age > 18) { return "Adult"; } else { return "Child"; } }).exceptionally(ex -> { System.out.println("Oops! We have an exception - " + ex.getMessage()); return "Unknown!"; }); System.out.println("Maturity : " + maturityFuture.get());

We can also combine successful and error callbacks using handle method, it takes two parameters, success value and exception. If the exception contains value means task thrown exception.

Conclusion

we have explored most importantly used and important concepts of CompletableFuture introduced in Java 8, and also we leaned differences between Future and CompletableFuture.

Keep Visiting Aegis Infoways Blog for More information about Java Development Solutions.