CompletableFuture
Future
RxJava
Observable
Java concurrency

Difference between CompletableFuture, Future and RxJava's Observable

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

In modern Java, asynchronous programming models have become essential for writing performant and non-blocking applications, especially in highly concurrent environments. Three primary constructs provide ways to handle asynchronous tasks in Java applications: CompletableFuture, Future, and RxJava's Observable. Each of these offers unique capabilities and trade-offs for asynchronous programming paradigms.

Futures

Future Interface

Introduced in Java 5, the Future interface represents the result of an asynchronous computation. It provides methods to check if the computation is complete, wait for its completion, and retrieve the result of the computation.

Characteristics

  • Blocking: Future.get() is a blocking operation. It waits for the computation to complete, potentially indefinitely unless a timeout is specified.
  • Cancellation Support: You can attempt to cancel a running task using Future.cancel().
  • Limited Functionality: It does not provide chaining of computations or exception handling.

Example Usage

java
1ExecutorService executor = Executors.newSingleThreadExecutor();
2Future<Integer> future = executor.submit(() -> {
3    TimeUnit.SECONDS.sleep(1);
4    return 42;
5});
6
7try {
8    Integer result = future.get();
9    System.out.println("Result: " + result);
10} catch (InterruptedException | ExecutionException e) {
11    e.printStackTrace();
12}

CompletableFuture

CompletableFuture Class

Introduced in Java 8, CompletableFuture extends Future with more capabilities and allows for more complex asynchronous programming patterns. It supports non-blocking operations, chaining, and allows combining multiple CompletableFutures.

Characteristics

  • Non-Blocking: Provides non-blocking methods like thenApply, thenAccept, etc., to build asynchronous pipelines.
  • Combining Futures: You can combine multiple CompletableFutures with methods like thenCombine, thenCompose, etc.
  • Exception Handling: Supports exception handling using methods like exceptionally and handle.
  • Manual Completion: complete and completeExceptionally methods allow manual completion.

Example Usage

java
1CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
2    return 42; // Assume this takes some time
3});
4
5future.thenApply(result -> result * 2)
6      .thenAccept(result -> System.out.println("Doubled Result: " + result))
7      .exceptionally(ex -> {
8          System.err.println("Exception: " + ex);
9          return null;
10      });

RxJava's Observable

ReactiveX and RxJava

RxJava is a Java implementation of ReactiveX, a library for composing asynchronous and event-based programs using observable sequences.

Characteristics

  • Event Streams: Observable can emit multiple items, unlike Future and CompletableFuture, which are single asynchronous computations.
  • Functional: Strongly embraces functional programming concepts, supporting map, flatMap, filter, etc.
  • Backpressure Handling: Provides mechanisms (such as Flowable) to handle backpressure.
  • Concurrency and Schedulers: Allows concurrency management using Schedulers.

Example Usage

java
1Observable<Integer> observable = Observable.just(1, 2, 3, 4)
2    .map(number -> number * 2)
3    .filter(number -> number > 4);
4
5observable.subscribe(System.out::println);

Comparison Table

FeatureFutureCompletableFutureRxJava's Observable
BlockingYesNoNo
ChainingNoYesYes
Multiple EmissionsNoNoYes
Exception HandlingManualBuilt-inBuilt-in
CancellationYesYesBuilt-in (Disposal)
Backpressure HandlingNoNoYes (with Flowable)
Combining Multiple Asynchronous OpsManual and limitedYesYes
Concurrency ControlExecutorService dependent ManualForkJoinPool or custom threadsSchedulers

Additional Considerations

When to Use

  • Future: Use when you need a simple solution for single asynchronous computations where blocking is acceptable, and you don't need chaining.
  • CompletableFuture: Ideal when you need to build complex asynchronous workflows with non-blocking chaining and exception handling.
  • RxJava: Suitable for applications that require handling multiple, possibly infinite, event streams with backpressure, functional composition, and concurrency control.

Integration

RxJava can integrate with traditional Java concurrency mechanisms. For instance, an Observable can be created from a CompletableFuture and vice-versa.

java
1CompletableFuture<String> completableFuture = createCompletableFuture();
2Observable<String> observable = Observable.fromFuture(completableFuture);
3
4observable.subscribe(System.out::println);

In modern Java applications, selecting the right paradigm for asynchronous programming is critical. Understanding the differences and use cases for CompletableFuture, Future, and RxJava's Observable empowers developers to write efficient and maintainable code tailored to the specific needs of their applications.


Course illustration
Course illustration

All Rights Reserved.