Runnable & Callable
About
Runnable and Callable are two interfaces in Java used for executing tasks in separate threads. Both are commonly used in multithreading and concurrency but have differences in functionality.
Runnable Interface
Definition
Runnableis an interface that represents a task to be executed by a thread.It has one method:
void run();It does not return a result and cannot throw checked exceptions.
Usage
A Runnable task can be executed by:
Creating a
ThreadobjectUsing
ExecutorService
Example 1: Implementing Runnable using a Class
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Executing task in thread: " + Thread.currentThread().getName());
    }
}
public class RunnableExample {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable()); // Assign task to thread
        thread.start(); // Starts a new thread
    }
}Here, the run() method is executed in a separate thread.
Example 2: Using Runnable with ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RunnableWithExecutor {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.submit(new MyRunnable());
        executor.shutdown();
    }
}ExecutorService is a thread pool manager that efficiently manages threads.
Callable Interface
Definition
Callable<T>is a functional interface introduced in Java 5.Unlike
Runnable, it returns a result and can throw checked exceptions.It has one method:
T call() throws Exception;The return type
Tallows it to be used for asynchronous computation.
Usage
A Callable task is executed using an ExecutorService and returns a Future<T> object.
Example 1: Implementing Callable with ExecutorService 
import java.util.concurrent.Callable;
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(2000); // Simulate delay
        return "Task completed by " + Thread.currentThread().getName();
    }
}import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable()); // Submit task
        System.out.println("Waiting for result...");
        String result = future.get(); // Blocks until the result is available
        System.out.println("Result: " + result);
        executor.shutdown();
    }
}Combining Runnable and Callable
If we want a Runnable but need a result, use Executors.callable(). This is useful when converting a Runnable to Callable.
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
public class RunnableToCallable {
    public static void main(String[] args) {
        Callable<Object> callableTask = Executors.callable(() -> 
            System.out.println("Runnable inside Callable"));
    }
}Using FutureTask (Runnable + Callable)
If we need both Runnable and Callable behaviors, use FutureTask<T>.FutureTask allows combining Runnable execution with Callable result handling.
import java.util.concurrent.FutureTask;
public class FutureTaskExample {
    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask = new FutureTask<>(() -> "Task completed!");
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("Result: " + futureTask.get());
    }
}Comparison Runnable and Callable
Introduced in
Java 1.0
Java 5
Method
void run()
T call() throws Exception
Return Value
No (void)
Yes (Generic T)
Exception Handling
Cannot throw checked exceptions
Can throw checked exceptions
Used With
Thread, ExecutorService
ExecutorService, Future
Best For
Executing tasks without result
Executing tasks that return a value
When to Use Runnable vs Callable vs Others
Runnable vs Callable vs OthersUse Case
Use
Why?
You need to execute a task without returning a result
Runnable
Runnable.run() has a void return type, making it ideal for simple background tasks.
You need to execute a task and return a result
Callable
Callable.call() returns a value, allowing you to capture the task's output.
You need to execute a task but may need to check its completion later
Callable + Future
Future<T> allows retrieving the result later without blocking the main thread.
You need to execute a task and get notified when it's completed
FutureTask
FutureTask allows combining Runnable and Callable, and it can be used as a Future.
You need to execute multiple independent tasks and wait for all to complete
ExecutorService + invokeAll()
invokeAll() submits multiple Callabletasks and waits for all results.
You need to execute multiple independent tasks and get the first completed result
ExecutorService + invokeAny()
invokeAny() submits multiple Callabletasks and returns the first successful result.
You need fine-grained control over thread execution (e.g., priority, interruption)
Thread + Runnable
Thread can directly manage execution but is less flexible than thread pools.
You need to handle checked exceptions in a background task
Callable
Callable.call() supports throwing checked exceptions.
You need to execute a task repeatedly at a fixed rate
ScheduledExecutorService
scheduleAtFixedRate() and scheduleWithFixedDelay() allow scheduled execution.
You need to execute CPU-intensive parallel tasks
ForkJoinPool
ForkJoinPool supports work-stealing, ideal for recursive and parallel tasks.
You need to execute stream operations in parallel
Parallel Streams
stream().parallel() splits work across multiple cores automatically.
You need to process a collection of tasks asynchronously
CompletableFuture
CompletableFuture allows non-blocking execution and chaining tasks together.
You need to execute a task with a timeout
ExecutorService + Future.get(timeout)
Future.get(timeout, TimeUnit.SECONDS)prevents indefinite waiting.
You need to execute a task in the background and monitor progress
CompletableFuture + thenApply/thenAccept
CompletableFuture allows progress tracking and callbacks.
You need non-blocking, event-driven async execution
CompletableFuture + SupplyAsync()
Asynchronous execution without blocking threads.
Last updated