Advanced Java October 06 ,2025

 Custom Implementation of CompletableFuture in Java

A Complete Step-by-Step Guide

Introduction

CompletableFuture in Java is a powerful tool for asynchronous programming.
It allows executing tasks in the background and processing results once they’re available — without blocking the main thread.

It’s part of the java.util.concurrent package and provides capabilities such as:

  •  Non-blocking asynchronous execution
  • Task chaining
  • Combining multiple futures

Building your own version helps you understand the core mechanics — how results are stored, how callbacks are triggered, and how chaining works.

Step 1 — Understand the Requirements

Before diving into code, let’s outline what our custom version needs to do.

A custom CompletableFuture must:

  1. Allow asynchronous task execution in a separate thread.
  2. Store the result for later retrieval.
  3. Support callbacks when computation completes.
  4. Support chaining or combining tasks sequentially.

Step 2 — Create Basic Structure

We start by creating a class CustomCompletableFuture<T> that will:

  • Store the result
  • Track completion status
  • Allow registering callbacks

Code:

import java.util.function.Consumer;

public class CustomCompletableFuture<T> {
    private T result;
    private boolean isDone = false;
    private Consumer<T> callback;

    // Completes the future and triggers any callback
    public synchronized void complete(T value) {
        if (!isDone) {
            result = value;
            isDone = true;
            notifyAll();
            if (callback != null) {
                callback.accept(result);
            }
        }
    }

    // Waits for the result if not yet available
    public synchronized T get() throws InterruptedException {
        while (!isDone) {
            wait();
        }
        return result;
    }

    // Registers a callback to be executed when result is ready
    public synchronized void thenAccept(Consumer<T> action) {
        if (isDone) {
            action.accept(result);
        } else {
            callback = action;
        }
    }
}

This class can store results, notify waiting threads, and execute callbacks when a task completes.

Step 3 — Implement Asynchronous Execution

We now add the ability to run tasks asynchronously.
To do that, we create a static method supplyAsync() that runs a task on a new thread.

Code:

public interface Task<U> {
    U get();
}

public static <U> CustomCompletableFuture<U> supplyAsync(Task<U> task) {
    CustomCompletableFuture<U> future = new CustomCompletableFuture<>();
    new Thread(() -> {
        U value = task.get();
        future.complete(value);
    }).start();
    return future;
}

Now, you can execute a task asynchronously and receive a future to track its result.

Step 4 — Test CustomCompletableFuture

Let’s test the implementation with a simple asynchronous task.

Code:

public class CompletableFutureTest {
    public static void main(String[] args) throws InterruptedException {
        CustomCompletableFuture<String> future = CustomCompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Task Completed";
        });

        future.thenAccept(result -> System.out.println("Callback: " + result));

        System.out.println("Main thread is not blocked.");

        String result = future.get();
        System.out.println("Result: " + result);
    }
}

Expected Output

Main thread is not blocked.
Callback: Task Completed
Result: Task Completed

Step 5 — Add Task Chaining Support

One of the main strengths of CompletableFuture is chaining tasks — executing one after another once the previous completes.
Let’s add that feature with a thenApply() method.

Code:

public <U> CustomCompletableFuture<U> thenApply(Task<U> nextTask) {
    CustomCompletableFuture<U> nextFuture = new CustomCompletableFuture<>();
    thenAccept(result -> {
        U nextResult = nextTask.get();
        nextFuture.complete(nextResult);
    });
    return nextFuture;
}

Now, you can run dependent asynchronous operations sequentially.

Step 6 — Test Task Chaining

Here’s a test for task chaining using our new thenApply() method.

Code:

public class CompletableFutureChainTest {
    public static void main(String[] args) throws InterruptedException {
        CustomCompletableFuture<Integer> future = CustomCompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 10;
        });

        future
            .thenApply(() -> 20)
            .thenAccept(result -> System.out.println("Chained Result: " + result));

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

Expected Output

Main thread continues...
Chained Result: 20

Step 7 — Add Exception Handling (Optional)

We can further enhance this by adding exception handling to manage runtime errors in asynchronous tasks.

This could include methods like:

  • exceptionally() to handle exceptions gracefully.
  • handle() to process results or errors.

However, for simplicity, this basic version focuses on the core logic of result handling, callbacks, and chaining.

Complete Code — Custom CompletableFuture Example

Here’s the entire working implementation you can copy and run:

import java.util.function.Consumer;

// Step 1 & 2 — Custom CompletableFuture Implementation
public class CustomCompletableFuture<T> {
    private T result;
    private boolean isDone = false;
    private Consumer<T> callback;

    public synchronized void complete(T value) {
        if (!isDone) {
            result = value;
            isDone = true;
            notifyAll();
            if (callback != null) {
                callback.accept(result);
            }
        }
    }

    public synchronized T get() throws InterruptedException {
        while (!isDone) {
            wait();
        }
        return result;
    }

    public synchronized void thenAccept(Consumer<T> action) {
        if (isDone) {
            action.accept(result);
        } else {
            callback = action;
        }
    }

    // Step 3 — Asynchronous Execution
    public interface Task<U> {
        U get();
    }

    public static <U> CustomCompletableFuture<U> supplyAsync(Task<U> task) {
        CustomCompletableFuture<U> future = new CustomCompletableFuture<>();
        new Thread(() -> {
            U value = task.get();
            future.complete(value);
        }).start();
        return future;
    }

    // Step 5 — Task Chaining
    public <U> CustomCompletableFuture<U> thenApply(Task<U> nextTask) {
        CustomCompletableFuture<U> nextFuture = new CustomCompletableFuture<>();
        thenAccept(result -> {
            U nextResult = nextTask.get();
            nextFuture.complete(nextResult);
        });
        return nextFuture;
    }
}

// Step 4 — Test Simple Future
class CompletableFutureTest {
    public static void main(String[] args) throws InterruptedException {
        CustomCompletableFuture<String> future = CustomCompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Task Completed";
        });

        future.thenAccept(result -> System.out.println("Callback: " + result));

        System.out.println("Main thread is not blocked.");

        String result = future.get();
        System.out.println("Result: " + result);
    }
}

// Step 6 — Test Task Chaining
class CompletableFutureChainTest {
    public static void main(String[] args) throws InterruptedException {
        CustomCompletableFuture<Integer> future = CustomCompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 10;
        });

        future
            .thenApply(() -> 20)
            .thenAccept(result -> System.out.println("Chained Result: " + result));

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

Real-World Use Cases

Custom CompletableFuture can be useful in:

  • Running background tasks without blocking the main thread
  • Chaining dependent asynchronous operations
  • Building lightweight reactive systems
  • rk or database calls in the background

Limitations

While educational, this version lacks:

  • Advanced combinators like thenCombine(), allOf(), or anyOf().
  • Timeout handling and cancellation support.
  • Proper error propagation.

In real-world production, you should use Java’s built-in CompletableFuture, which provides all these features safely and efficiently.

Summary

  • CompletableFuture helps manage asynchronous tasks elegantly.
  • Building a CustomCompletableFuture improves understanding of:
    • Threads and synchronization
    • Result propagation
    • Callback mechanisms
    • Task chaining logic

This custom version lays the foundation for mastering asynchronous and reactive programming in Java.

 

Next Blog- Custom Implementation of LinkedBlockingQueue in Java

Sanjiv
0

You must logged in to post comments.

Related Blogs

Generics P...
Advanced Java August 08 ,2025

Generics Part- 2

Collection...
Advanced Java July 07 ,2025

Collections Framewor...

Mastering...
Advanced Java August 08 ,2025

Mastering Java Multi...

Annotation...
Advanced Java August 08 ,2025

Annotations

Java Multi...
Advanced Java August 08 ,2025

Java Multithreading...

Java Memor...
Advanced Java August 08 ,2025

Java Memory Manageme...

Java Lambd...
Advanced Java August 08 ,2025

Java Lambda Expressi...

Java Funct...
Advanced Java August 08 ,2025

Java Functional Inte...

Java Strea...
Advanced Java August 08 ,2025

Java Stream API

JDBC (Java...
Advanced Java August 08 ,2025

JDBC (Java Database...

JDBC (Java...
Advanced Java September 09 ,2025

JDBC (Java Database...

Annotation...
Advanced Java August 08 ,2025

Annotations

Generics
Advanced Java August 08 ,2025

Generics

Java I/O (...
Advanced Java August 08 ,2025

Java I/O (NIO)

Introducti...
Advanced Java September 09 ,2025

Introduction to Desi...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Other Prin...
Advanced Java September 09 ,2025

Other Principles Beh...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Builder De...
Advanced Java September 09 ,2025

Builder Design Patte...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Chain of R...
Advanced Java September 09 ,2025

Chain of Responsibil...

Command De...
Advanced Java September 09 ,2025

Command Design Patte...

Interprete...
Advanced Java September 09 ,2025

Interpreter Design P...

Iterator D...
Advanced Java September 09 ,2025

Iterator Design Patt...

Mediator D...
Advanced Java September 09 ,2025

Mediator Design Patt...

Memento De...
Advanced Java September 09 ,2025

Memento Design Patte...

Observer D...
Advanced Java September 09 ,2025

Observer Design Patt...

State Desi...
Advanced Java September 09 ,2025

State Design Pattern...

Strategy D...
Advanced Java September 09 ,2025

Strategy Design Patt...

Template M...
Advanced Java September 09 ,2025

Template Method Desi...

Visitor De...
Advanced Java September 09 ,2025

Visitor Design Patte...

Prototype...
Advanced Java September 09 ,2025

Prototype Design Pat...

Java 8+ Fe...
Advanced Java October 10 ,2025

Java 8+ Features

SOLID Prin...
Advanced Java October 10 ,2025

SOLID Principles in...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

How Iterat...
Advanced Java October 10 ,2025

How Iterators Work i...

How Concur...
Advanced Java October 10 ,2025

How ConcurrentHashMa...

Comparable...
Advanced Java October 10 ,2025

Comparable vs Compar...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Semaphore...
Advanced Java October 10 ,2025

Semaphore in Java

ExecutorSe...
Advanced Java October 10 ,2025

ExecutorService in J...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Producer-C...
Advanced Java October 10 ,2025

Producer-Consumer Pr...

Implementi...
Advanced Java October 10 ,2025

Implementing a Custo...

Busy Spin
Advanced Java October 10 ,2025

Busy Spin

Serializat...
Advanced Java October 10 ,2025

Serialization and De...

Segment Lo...
Advanced Java October 10 ,2025

Segment Locking in J...

Tree Bins...
Advanced Java October 10 ,2025

Tree Bins in Java

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Get In Touch

G06, Kristal Olivine Bellandur near Bangalore Central Mall, Bangalore Karnataka, 560103

+91-8076082435

techiefreak87@gmail.com