Java CompletableFuture thenAcceptBoth() Method

The CompletableFuture class in Java provides the thenAcceptBoth() method to perform an action when two CompletableFuture instances complete.

Introduction

The CompletableFuture.thenAcceptBoth() method is used to perform a specified action when both CompletableFuture instances complete. It takes a BiConsumer that processes the results of both futures and returns a new CompletableFuture that is completed when the action is finished.

thenAcceptBoth Method Syntax

The syntax for the thenAcceptBoth method is as follows:

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
  • The method takes two parameters:
    • other of type CompletionStage<? extends U>, which is the other CompletableFuture to combine with.
    • action of type BiConsumer<? super T, ? super U>, which represents the action to perform on the results of both futures.
  • The method returns a new CompletableFuture<Void> that is completed when the action is finished.

Examples

Example 1: Printing Results of Two Asynchronous Tasks

In a scenario where you have two CompletableFuture instances that complete with different results, you might want to print both results when both futures complete.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class ThenAcceptBothExample {
    public static void main(String[] args) {
        // Create two CompletableFuture instances
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

        // Print the results of both futures using thenAcceptBoth
        future1.thenAcceptBoth(future2, (result1, result2) -> System.out.println(result1 + " " + result2));

        // Wait for the futures to complete
        try {
            future1.get();
            future2.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Output:

Hello World

Example 2: Task Management System

In a task management system, you might want to log the completion of two tasks when both tasks are completed.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class TaskManagementSystem {
    public static void main(String[] args) {
        // Create two CompletableFuture instances for tasks
        CompletableFuture<Task> taskFuture1 = CompletableFuture.supplyAsync(() -> new Task("Complete project report", 2));
        CompletableFuture<Task> taskFuture2 = CompletableFuture.supplyAsync(() -> new Task("Email client updates", 1));

        // Log the completion of both tasks using thenAcceptBoth
        taskFuture1.thenAcceptBoth(taskFuture2, (task1, task2) ->
            System.out.println("Tasks completed:\n" + task1 + "\n" + task2)
        );

        // Wait for the tasks to complete
        try {
            taskFuture1.get();
            taskFuture2.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class Task {
    private String description;
    private int priority;

    public Task(String description, int priority) {
        this.description = description;
        this.priority = priority;
    }

    @Override
    public String toString() {
        return description + " (Priority: " + priority + ")";
    }
}

Output:

Tasks completed:
Complete project report (Priority: 2)
Email client updates (Priority: 1)

Example 3: Merging Data from Two Sources

In a data processing application, you might want to merge data from two different sources when both sources have completed their data fetch.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class DataMergeExample {
    public static void main(String[] args) {
        // Create two CompletableFuture instances for fetching data
        CompletableFuture<String> dataFuture1 = CompletableFuture.supplyAsync(() -> "Data from source 1");
        CompletableFuture<String> dataFuture2 = CompletableFuture.supplyAsync(() -> "Data from source 2");

        // Merge the data from both sources using thenAcceptBoth
        dataFuture1.thenAcceptBoth(dataFuture2, (data1, data2) ->
            System.out.println("Merged data: " + data1 + " + " + data2)
        );

        // Wait for the data fetches to complete
        try {
            dataFuture1.get();
            dataFuture2.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Output:

Merged data: Data from source 1 + Data from source 2

Conclusion

The CompletableFuture.thenAcceptBoth() method in Java is used for performing actions that depend on the completion of two CompletableFuture instances. It is particularly useful in scenarios where you need to process and combine the results of multiple asynchronous computations, such as printing results, logging task completions, or merging data from different sources.

Comments