Java Multiple Threading using CompletableFuture and allOf Method: A Comprehensive Guide with Examples

2 min read

Java Multiple Threading using CompletableFuture and allOf Method: A Comprehensive Guide with Examples

Java introduced the CompletableFuture class in version 8 as part of the java.util.concurrent package. This class enables asynchronous programming and allows developers to perform tasks concurrently without blocking the main thread. Among the methods provided by CompletableFuture, the allOf method is particularly useful for waiting for the completion of multiple asynchronous tasks.

This article explains the use of CompletableFuture and its allOf method with a detailed example.

What is CompletableFuture?

A CompletableFuture represents a computation result that may be completed in the future. It provides methods to:

  1. Run computations asynchronously.
  2. Combine multiple computations.
  3. Handle results and exceptions of asynchronous tasks.

What is allOf()?

The CompletableFuture.allOf() method is used to combine multiple CompletableFuture instances into a single CompletableFuture. It waits until all supplied futures have completed and provides a way to handle their results.

Use Case: Fetching Data from Multiple Sources

Imagine a scenario where you need to fetch data from multiple APIs concurrently. Using CompletableFuture.allOf(), you can run each fetch task in parallel and wait for all of them to complete.

Example: Using CompletableFuture and allOf

Below is an example of fetching user data from multiple simulated sources using CompletableFuture.allOf().

Step 1: Simulating an API Call

Create a method that mimics an asynchronous API call with a delay:

import java.util.concurrent.CompletableFuture;

public class DataFetcher {
    public static CompletableFuture<String> fetchDataAsync(String sourceName) {
        return CompletableFuture.supplyAsync(() -> {
            simulateDelay();
            return "Data from " + sourceName;
        });
    }

    private static void simulateDelay() {
        try {
            Thread.sleep(2000); // Simulates a 2-second delay
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Step 2: Fetching Data from Multiple Sources

Combine multiple asynchronous API calls and wait for their completion:

import java.util.concurrent.CompletableFuture;

public class AllOfExample {
    public static void main(String[] args) {
        System.out.println("Fetching data from multiple sources...");

        // Create individual CompletableFutures
        CompletableFuture<String> source1 = DataFetcher.fetchDataAsync("Source 1");
        CompletableFuture<String> source2 = DataFetcher.fetchDataAsync("Source 2");
        CompletableFuture<String> source3 = DataFetcher.fetchDataAsync("Source 3");

        // Combine all futures using allOf
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(source1, source2, source3);

        // Wait for all tasks to complete
        allFutures.join();

        // Process the results
        try {
            System.out.println(source1.get());
            System.out.println(source2.get());
            System.out.println(source3.get());
        } catch (Exception e) {
            System.err.println("Error occurred: " + e.getMessage());
        }

        System.out.println("All tasks completed.");
    }
}

Detailed Breakdown

  1. Asynchronous Execution:
    • DataFetcher.fetchDataAsync is called for each source. The tasks run in parallel on separate threads.
  2. Combining Futures:
    • CompletableFuture.allOf(source1, source2, source3) ensures all tasks complete before proceeding.
  3. Blocking Until Completion:
    • allFutures.join() blocks the main thread until all individual futures are finished.
  4. Extracting Results:
    • source1.get(), source2.get(), and source3.get() retrieve results from individual futures.

Expected Output

Fetching data from multiple sources...
Data from Source 1
Data from Source 2
Data from Source 3
All tasks completed.

Advanced Usage

Processing Results Together

You can collect all results into a list:

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class AllOfExampleAdvanced {
    public static void main(String[] args) {
        List<CompletableFuture<String>> futures = List.of(
            DataFetcher.fetchDataAsync("Source 1"),
            DataFetcher.fetchDataAsync("Source 2"),
            DataFetcher.fetchDataAsync("Source 3")
        );

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        );

        List<String> results = allFutures.thenApply(v -> futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList())
        ).join();

        results.forEach(System.out::println);
    }
}

Handling Exceptions

Use exceptionally to handle errors gracefully:

source1.exceptionally(throwable -> "Failed to fetch Source 1: " + throwable.getMessage());

Key Benefits

  1. Parallel Processing:
    • Tasks run concurrently, reducing overall execution time.
  2. Flexible Result Handling:
    • Collect and process results once all tasks are complete.
  3. Error Handling:
    • Combine error handling with asynchronous processing.

Conclusion

Java’s CompletableFuture and its allOf() method offer a powerful way to execute and manage multiple asynchronous tasks. By applying this technique, you can optimize application performance and handle complex workflows involving parallel processing.

🤞 Never miss a story from us, get weekly updates to your inbox!

One Reply to “Java Multiple Threading using CompletableFuture and allOf Method: A…”

Comments are closed.