Java- future and callable(runnable) vs completable future and executor framework and much more
Callable is the task to be executed.
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
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:
- For high-performance production systems, you should pass an explicit
ExecutorServiceinto yourCompletableFuturecalls. By default,CompletableFutureruns tasks on theForkJoinPool.commonPool(), which is shared globally by the entire JVM. If one task hangs, it can starve your whole application
ref:
- You can fix the point 1 probleme easily by injecting your custom thread pool.
ref:
reference for executor framework
[embed]
Important interview questions:
⚠️ 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