Runnable vs Callable in Java: Key Differences and Usage
Runnable is a functional interface for tasks that do not return a result and cannot throw checked exceptions, while Callable allows returning a result and throwing checked exceptions. Both are used to run code in threads, but Callable is preferred when you need a result or exception handling.Quick Comparison
This table summarizes the main differences between Runnable and Callable interfaces in Java.
| Feature | Runnable | Callable |
|---|---|---|
| Return Type | void (no result) | Generic type |
| Exception Handling | Cannot throw checked exceptions | Can throw checked exceptions |
| Method | run() | call() |
| Introduced In | Java 1.0 | Java 5 |
| Usage | For tasks without result | For tasks with result or exceptions |
| Used With | Thread, ExecutorService | ExecutorService |
Key Differences
Runnable is a simple interface with a single method run() that does not return any value and cannot throw checked exceptions. It is mainly used for tasks that perform actions without needing to return a result or report errors explicitly.
On the other hand, Callable has a call() method that returns a value of a generic type and can throw checked exceptions. This makes it suitable for tasks where you want to get a result back or handle exceptions properly.
Another difference is in usage: Runnable can be executed by a Thread directly, while Callable requires an ExecutorService to submit the task and get a Future representing the result.
Runnable Code Example
public class RunnableExample implements Runnable { @Override public void run() { System.out.println("Runnable task is running"); } public static void main(String[] args) { Thread thread = new Thread(new RunnableExample()); thread.start(); } }
Callable Equivalent
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 CallableExample implements Callable<String> { @Override public String call() throws Exception { return "Callable task result"; } public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(new CallableExample()); try { String result = future.get(); System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } }
When to Use Which
Choose Runnable when your task does not need to return any result and does not throw checked exceptions. It is simpler and can be used directly with Thread.
Choose Callable when you need to get a result from the task or handle checked exceptions properly. It works with ExecutorService and returns a Future to retrieve the result asynchronously.
In modern Java concurrent programming, prefer Callable for tasks requiring results or error handling, and use Runnable for simple fire-and-forget tasks.