What is the difference between Runnable and Callable?
Table of Contents
Introduction
In Java, both **Runnable**
and **Callable**
are functional interfaces that represent tasks to be executed by multiple threads. While both are used for concurrent programming and multithreading, they have different capabilities, especially when it comes to handling the results of tasks and exceptions. Understanding the differences between these two interfaces is crucial for deciding which one to use in various scenarios when working with Java concurrency.
In this article, we will explore the key differences between **Runnable**
and **Callable**
, their usage, and when to use one over the other.
Key Differences Between Runnable and Callable
1. Return Value
The most significant difference between **Runnable**
and **Callable**
is that **Runnable**
does not return a result, while **Callable**
can return a result.
- Runnable: The
**run()**
method in the**Runnable**
interface does not return anything (i.e., it has a void return type). - Callable: The
**call()**
method in the**Callable**
interface returns a value of a specified type, which allows you to retrieve the result of the task once it completes.
Example: Return Value in Callable
2. Exception Handling
- Runnable: The
**run()**
method in**Runnable**
cannot throw any checked exceptions. It can only throw unchecked exceptions (i.e.,RuntimeException
and its subclasses). - Callable: The
**call()**
method in**Callable**
can throw checked exceptions, which gives it more flexibility when handling exceptions during the execution of a task.
Example: Exception Handling in Callable
3. Execution in Thread
- Runnable: You can execute
**Runnable**
tasks using**Thread**
or**ExecutorService**
. Since it doesn't return a result, the main focus is on the side effects of the task (e.g., modifying shared variables or performing actions). - Callable:
**Callable**
tasks are usually executed by**ExecutorService**
, which can manage the task and allow retrieval of the result.**ExecutorService**
provides the**submit()**
method to submitCallable
tasks and returns a**Future**
object that can be used to get the result or check the status of the task.
Example: Executing Runnable with Thread
Example: Executing Callable with ExecutorService
4. Use Cases
- Runnable: Ideal when you do not need to return a result and just need to execute a task concurrently. Use
**Runnable**
for tasks like logging, updating a shared resource, or performing background operations where the result is not needed. - Callable: Suitable when you need a result from the task or need to handle exceptions.
**Callable**
is often used in cases where the task needs to perform computations and return a value, or when dealing with tasks that might throw checked exceptions.
Summary of Differences
Feature | Runnable | Callable |
---|---|---|
Return Value | No return value (void) | Returns a result (can specify type) |
Exception Handling | Cannot throw checked exceptions | Can throw checked exceptions |
Execution Context | Usually executed by Thread or ExecutorService | Usually executed by ExecutorService with a Future |
Use Case | Tasks that don’t require a result | Tasks that need to return a result or throw checked exceptions |
Conclusion
Both **Runnable**
and **Callable**
are designed to represent tasks that can be executed in separate threads. The choice between **Runnable**
and **Callable**
depends on whether you need a result from the task and whether the task can throw exceptions. Use **Runnable**
when the task doesn't need to return a result, and **Callable**
when you need to retrieve a result or handle checked exceptions.
In most modern concurrent applications, you would typically use **Callable**
with **ExecutorService**
to take advantage of the ability to retrieve results and manage tasks more flexibly.