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 submit Callable 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

FeatureRunnableCallable
Return ValueNo return value (void)Returns a result (can specify type)
Exception HandlingCannot throw checked exceptionsCan throw checked exceptions
Execution ContextUsually executed by Thread or ExecutorServiceUsually executed by ExecutorService with a Future
Use CaseTasks that don’t require a resultTasks 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.

Similar Questions