← Back to list

Java- future and callable(runnable) vs completable future and executor framework and much more

Callable is the task to be executed.

tech_hi_tech_dosto · 2026-06-21 23:54 · 0 claps · 4.6 min read
#future #callable #runnable #completablefuture #executorservice
Open on Medium ↗

Java- future and callable(runnable) vs completable future and executor framework and much more

Callable is the task to be executed.

Future represents the handle to that task’s result.

Callable is the task to be executed, Future represents the handle to that task’s result, and CompletableFuture is an advanced evolution of Future that supports asynchronous, non-blocking callback chains and task composition.

Key Differences

Task Definition vs. Asynchronous Orchestration

Callable: It only defines the workload using its single call() method. It does not know how or when it will run. You must hand it off to an execution framework (like an ExecutorService) to get a Future back.

CompletableFuture: It represents the lifecycle of the asynchronous result itself. It provides built-in factory methods like supplyAsync() to both accept a task and kick off background processing immediately.

Blocking vs. Non-Blocking (Callbacks)

Callable: To get the result of a submitted Callable, you rely on a standard Future. Calling future.get() halts the calling thread completely until the task finishes.

CompletableFuture: It allows a purely reactive, non-blocking programming model. You register listener callbacks using methods like thenAccept() or thenRun(). The thread continues immediately, and the callback fires automatically upon completion.

Composition and Chaining

Callable: Independent tasks cannot be easily glued together. If Task B depends on Task A's result, you must blockingly wait for Task A, fetch its output, and manually pass it to Task B.

CompletableFuture: You can easily chain complex async workflows using its fluent API. For example, thenApply() transforms results, and thenCompose() seamlessly handles nested dependencies

Exception Handling

Callable: The call() method supports checked exceptions directly in its signature. However, the calling code must manage this with clunky try-catch wrappers around the blocking .get() call.

CompletableFuture: It bypasses checked exceptions by design and embeds error recovery straight into the functional chain via methods like exceptionally() or handle()

Code Examples

The Callable Approach (Classic Blocking)

ExecutorService executor = Executors.newFixedThreadPool(2);

// Define the task
Callable<String> task = () -> {
    Thread.sleep(1000);
    return "Data from database";
};

// Submit and get a Future placeholder
Future<String> future = executor.submit(task);

// Blocker: Main thread stops here until the data arrives
try {
    String result = future.get(); 
    System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
executor.shutdown();

The CompletableFuture Approach (Modern Non-Blocking Pipeline)

// Kicks off asynchronously immediately using ForkJoinPool.commonPool()
CompletableFuture.supplyAsync(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) {}
    return "Data from database";
})
// Transform the data without blocking
.thenApply(data -> data + " - processed") 
// Consume the final data asynchronously once it's ready
.thenAccept(finalResult -> System.out.println(finalResult)) 
// Handle any exceptions anywhere in the pipeline gracefully
.exceptionally(ex -> {
    System.out.println("Failed: " + ex.getMessage());
    return null;
});

reference for future, callable and completable future.

ref 1

[embed]

ref 2

[embed]Java Multithreading Part 6: Callable vs Future vs CompletableFuture — Key Differences with Examples When to use Future, when to use CompletableFuture, and how to chain async tasks in modern Javamedium.com

Executor service framework -

ExecutorService manages thread allocation and task execution, whereas CompletableFuture focuses on composing, chaining, and handling the results of asynchronous workflows. They are not mutually exclusive; in fact, CompletableFuture often uses an underlying ExecutorService to run its background tasks.

Code examples-

ExecutorService: Task-Centric Execution:

ExecutorService decouples task submission from how the threads run. It is excellent when you have isolated tasks that you want to dump into a managed thread pool. However, to get a result, your main thread must call a blocking method

// Setup a thread pool
ExecutorService executor = Executors.newFixedThreadPool(2);

// Submit a task and get a basic Future
Future<String> future = executor.submit(() -> {
    return "Data from database";
});

// CRITICAL LIMITATION: This blocks the current thread until finished
try {
    String result = future.get(); 
    System.out.println(result);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    executor.shutdown();
}

CompletableFuture: Reactive Async Workflows:

Introduced in Java 8, CompletableFuture implements the CompletionStage interface. It acts as a dataflow graph: when step A completes, automatically trigger step B, then step C, without blocking any threads along the way.

CompletableFuture.supplyAsync(() -> "User_123")
    // Chain step 1: Fetch details (runs when the above finishes)
    .thenApply(userId -> fetchUserProfile(userId)) 
    // Chain step 2: Handle the outcome or errors reactively
    .thenAccept(profile -> System.out.println("Profile loaded: " + profile))
    // Clean exception handling
    .exceptionally(ex -> {
        System.out.println("Failed: " + ex.getMessage());
        return null;
    });

Tips:

  1. For high-performance production systems, you should pass an explicit ExecutorService into your CompletableFuture calls. By default, CompletableFuture runs tasks on the ForkJoinPool.commonPool(), which is shared globally by the entire JVM. If one task hangs, it can starve your whole application

ref:

[embed]Asynchronous Execution in Java using CompletableFuture and ExecutorService Asynchronous execution is a programming concept that allows a program to perform multiple tasks simultaneously. In…medium.com

  1. You can fix the point 1 probleme easily by injecting your custom thread pool.

ref:

[embed]Java Concurrency (Multithreading): CompletableFuture v/s ExecutorService Photo by Obie Fernandez on Unsplashmedium.com

reference for executor framework

[embed]

Important interview questions:

[embed]#javainterview #completablefuture #concurrency #interviewprep #softwareengineer #backend | ANKITA… Got asked this in an interview last week: "You need to call 3 external APIs and merge the responses. How would you do…www.linkedin.com

⚠️ Update Mode: This post is a draft I decided to share now rather than waiting for perfection. It’s a work in progress — some parts may need more research or editing. Stay tuned for updates!


메타데이터
post_id
01378a32cff8
slug
java-future-and-callable-runnable-vs-completable-future-and-executor-framework-and-much-more-01378a32cff8
url
https://medium.com/@techhitechdosto/java-future-and-callable-runnable-vs-completable-future-and-executor-framework-and-much-more-01378a32cff8
canonical_url
https://medium.com/@techhitechdosto/java-future-and-callable-runnable-vs-completable-future-and-executor-framework-and-much-more-01378a32cff8
author_url
https://medium.com/@techhitechdosto
status
ok
fetched_at
2026-07-24 04:20:45