Thread Java 30 days- day 13 (CompletableFuture)

paul chou
2 min readOct 31, 2023

--

It is been a while since I last updated because I’ve been quite busy with work. I’ve recently developed a small tool related to threads, so today I’d like to introduce CompletableFuture.

CompletableFuture is introduced in Java 8 as part of the java.util.concurrent package. Java 8 introduced many new features and enhancements for handling asynchronous and concurrent programming, and CompletableFuture is one of the key additions in this regard.

Example:

import java.util.concurrent.CompletableFuture;

public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// Simulate a time-consuming operation, e.g., downloading a file
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Operation completed";
});

future.thenAccept(result -> {
System.out.println("Operation result: " + result);
});

System.out.println("Main thread continues to execute");
}
}

Purpose and Usage:

Runnable:

  • Runnable is used to represent a task or action that can be executed in a separate thread. It defines a single run method that you implement with the code you want to run asynchronously.
  • It’s simple and often used to create a new thread, execute a task, and that’s it. It doesn’t provide built-in mechanisms for handling the result of the task or dealing with exceptions.

CompletableFuture:

  • CompletableFuture is a class in Java that is specifically designed for handling asynchronous operations and managing their results. It provides a higher level of functionality compared to Runnable.
  • It allows you to create complex workflows of asynchronous operations, combine and chain them, specify how to handle their results, and deal with exceptions that might occur during the operation.

COMPLETABLEFUTURE COMBINE EXECUTORSERVICE

The difference between using CompletableFuture with an ExecutorService and without it lies in how you control and manage the execution of asynchronous tasks:

Using ExecutorService:

  • When you use CompletableFuture with an ExecutorService by passing it to methods like supplyAsync or runAsync, you have explicit control over which threads execute the asynchronous operations and how many threads are used.
  • You can create your own custom ExecutorService with specific configurations to meet your application's requirements, such as thread pool size and behavior.

Without ExecutorService:

  • If you don’t pass an ExecutorService to CompletableFuture methods, it will use the default ForkJoinPool for executing asynchronous operations.
  • This approach is more straightforward, and you don’t need to configure a custom ExecutorService.
  • It’s suitable for most cases where you don’t require fine-grained control over thread management.

Using CompletableFuture with an ExecutorService provides explicit control over thread execution and is suitable when you need to fine-tune concurrency. On the other hand, not using an ExecutorService lets you rely on the default ForkJoinPool, which is more convenient for many typical scenarios without complex thread management needs.

--

--