Callable and Future in Java
Introduction
In Java multithreading, the traditional way to run tasks asynchronously is by implementing the Runnable interface. However, Runnable has two major limitations:
- Its
run()method returnsvoid(it cannot return a result). - It cannot throw checked exceptions.
To overcome these limitations, Java 1.5 introduced the Callable interface and the Future interface inside the java.util.concurrent package.
1. The Callable Interface
Callable<V> is a generic interface containing a single method: call().
Unlike Runnable, the call() method returns a generic value V and is allowed to throw checked exceptions.
import java.util.concurrent.Callable;
// A task that returns an Integer and can throw an exception
public class FactorialTask implements Callable<Integer> {
private int number;
public FactorialTask(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
if (number < 0) {
throw new IllegalArgumentException("Number must be positive.");
}
int fact = 1;
for (int i = 1; i <= number; i++) {
fact *= i;
Thread.sleep(100); // Simulating heavy computation
}
return fact;
}
}
2. The Future Interface
When you submit a Callable to an ExecutorService, the execution happens asynchronously on a background thread. Since the result isn't available immediately, the executor gives you back a Future object.
A Future acts as a placeholder or a "promise" that will eventually hold the result of the Callable once the background thread finishes its work.
Key Methods of Future:
get(): Blocks the current thread and waits until the task is complete, then returns the result.isDone(): Returnstrueif the task is completed (successfully, cancelled, or threw an exception).cancel(boolean mayInterrupt): Attempts to cancel the execution of the task.
Code Example: Putting it all together
Here is how you execute a Callable using an ExecutorService and retrieve the result using a Future.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableFutureExample {
public static void main(String[] args) {
// 1. Create a Thread Pool
ExecutorService executor = Executors.newFixedThreadPool(2);
// 2. Create the Callable Task
Callable<Integer> task = new FactorialTask(5);
// 3. Submit the task to the executor. It returns a Future immediately.
System.out.println("Submitting Task...");
Future<Integer> futureResult = executor.submit(task);
System.out.println("Task submitted. Main thread is free to do other things.");
try {
// 4. Retrieve the result.
// The get() method will BLOCK until the background thread finishes.
Integer result = futureResult.get();
System.out.println("Result retrieved: " + result);
} catch (InterruptedException e) {
System.err.println("Thread was interrupted while waiting.");
} catch (ExecutionException e) {
System.err.println("An exception occurred inside the Callable: " + e.getCause());
} finally {
// Always shut down the executor
executor.shutdown();
}
}
}
Summary Comparison
| Feature | Runnable | Callable |
| :--- | :--- | :--- |
| Method | run() | call() |
| Return Type | void | Generic <V> |
| Checked Exceptions | Cannot throw | Can throw Exception |
| Execution | Thread, ExecutorService | ExecutorService only |