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:
- Run computations asynchronously.
- Combine multiple computations.
- 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
- Asynchronous Execution:
DataFetcher.fetchDataAsync
is called for each source. The tasks run in parallel on separate threads.
- Combining Futures:
CompletableFuture.allOf(source1, source2, source3)
ensures all tasks complete before proceeding.
- Blocking Until Completion:
allFutures.join()
blocks the main thread until all individual futures are finished.
- Extracting Results:
source1.get()
,source2.get()
, andsource3.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
- Parallel Processing:
- Tasks run concurrently, reducing overall execution time.
- Flexible Result Handling:
- Collect and process results once all tasks are complete.
- 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.
One Reply to “Java Multiple Threading using CompletableFuture and allOf Method: A…”
Comments are closed.