Java Concurrency

What is the difference between Callable and Runnable?

Runnable represents work that runs without returning a result. Callable represents work that can return a result and throw checked exceptions, usually accessed through a Future.

ConcurrencyExecutorServiceFuture

The Short Answer

Runnable is for a task that performs work but does not return a result.

Callable is for a task that performs work and returns a result, or throws a checked exception.

The Real Problem

Imagine submitting 100 background tasks to check inventory, price products, call services, or process files.

Sometimes you only care that the task runs. Other times, you need the answer back. That is the core difference.

Runnable

Task runs
Does work
No result returned

Good for fire-and-forget work like logging, cleanup, or sending a notification.

Callable

Task runs
Computes something
Result comes back through Future

Good when the caller needs the result, status, or failure of the background task.

Runnable Example

java
Runnable task = () -> {
    System.out.println("Sending email...");
};

executorService.submit(task);

This task runs, but there is no meaningful return value. If the task completes successfully, the returned Future does not contain a useful business result.

Callable Example

java
Callable<Integer> task = () -> {
    return calculateScore();
};

Future<Integer> future = executorService.submit(task);

Integer score = future.get();

Here the task produces an Integer. The Future acts like a handle to the result that will be available later.

The Mental Model

Runnable says: “Run this work.” Callable says: “Run this work and give me the answer later.”

Why Future Matters

When you submit a Callable to an ExecutorService, the result is not available immediately. The task may still be running on another thread.

A Future lets you check, wait, cancel, or retrieve the result once the task completes.

java
Future<String> future = executorService.submit(() -> {
    return fetchUserProfile(userId);
});

if (future.isDone()) {
    String profile = future.get();
}

Be careful: calling get() can block the current thread until the result is ready.

Important Interview Detail: Checked Exceptions

Runnable defines:

java
public interface Runnable {
    void run();
}

Notice that run() does not declare throws Exception.

Callable defines:

java
public interface Callable<V> {
    V call() throws Exception;
}

Notice that call() explicitly allows checked exceptions through throws Exception.

Runnable's run()method cannot throw checked exceptions directly. Callable's call() method can.

java
Callable<String> task = () -> {
    return readFileFromDisk(); // can throw checked exception
};

This makes Callable more natural for tasks like file reads, network calls, database lookups, or service calls where failure is part of the contract.

Common Mistake

A common mistake is using Runnable for a task where the caller later needs the result.

java
// Awkward: where does the result go?
Runnable task = () -> {
    int value = calculateScore();
};

Use Callable instead:

java
Callable<Integer> task = () -> calculateScore();

When Should You Use Each?

Use Runnable when...

  • You do not need a return value
  • The task is fire-and-forget
  • You are doing logging, cleanup, or notification work

Use Callable when...

  • You need a result
  • You need exception handling through Future
  • You are submitting work to an ExecutorService

Common Interview Follow-Ups

Can Runnable return a value?

Not directly. Runnable's run method returns void. You can mutate shared state, but that is usually less clean than using Callable.

Can Callable be used with ExecutorService?

Yes. Callable is commonly submitted to ExecutorService, which returns a Future holding the eventual result.

Does Future.get() block?

Yes. If the task has not completed yet, get() waits until the result is available or an exception occurs.

Is Callable a replacement for Runnable?

No. Callable is useful when you need a result or checked exception support. Runnable is still perfect for simple fire-and-forget work.

Final Takeaway

Use Runnable when you only need work to run. Use Callable when you need the result, failure, or completion outcome of that work.