48 Differences Between Callable and Runnable

48 Differences Between Callable and Runnable #

Hello, welcome to lesson 48. In this lesson, we will discuss the differences between Callable and Runnable.

Why do we need Callable? Drawbacks of Runnable #

First, let’s discuss why we need Callable. To answer this question, let’s first look at the drawbacks of the existing Runnable.

Unable to return a value #

The first drawback is that Runnable cannot return a value. Although there are ways to work around this, such as writing the result to a log file or modifying a shared object to store the result, these workarounds can be complicated and inefficient.

In many cases, when executing a thread, we want to obtain the result of the task, that is, we need the return value. For example, when making network requests or querying a database. However, Runnable cannot return a value, which is a significant limitation.

Unable to throw checked exceptions #

The second drawback is that Runnable cannot throw checked exceptions, as shown in the code below:

public class RunThrowException {

   /**
    * A normal method can throw exceptions and declare 'throws' in the method signature.
    */
   public void normalMethod() throws Exception {
       throw new IOException();
   }

   Runnable runnable = new Runnable() {

       /**
        * The run method cannot declare 'throws' and cannot throw checked exceptions 
        * unless caught using a try-catch block.
        */
       @Override
       public void run() {
           try {
               throw new IOException();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
}

In this code snippet, we have two methods. The first method is a normal method called normalMethod, and as you can see, it throws an exception in its method signature and also throws IOException within the method body.

Then, in the below code, we create a new Runnable object and override its run method. We cannot declare the throw of an exception in the method signature of this run method. Also, we cannot throw a checked exception within this run method unless we catch it with a try-catch block.

These are the two major drawbacks of Runnable.

Why do these drawbacks exist? #

Why do these drawbacks exist? Let’s look at the definition of the Runnable interface:

public interface Runnable {
   public abstract void run();
}

The code is concise. Runnable is an interface with only one method, public abstract void run(). This method specifies that the return type of run() is void and there are no declared exceptions. Therefore, when implementing and overriding this method, we cannot change the return type or modify the exception throwing, as these modifications are not allowed by the language syntax.

Reviewing the many code snippets from previous lessons, we never encountered a situation where we could return a value from the run method.

Why is Runnable designed like this? #

Let’s dive deeper into this question. Why did Java design Runnable like this?

Suppose the run() method could return a value or throw an exception. It still wouldn’t help because we would have no way to catch and handle it externally. This is because the class that calls the run() method (such as Thread or a thread pool) is provided by Java and not written by us.

Even if it could have a return value, it would be challenging to utilize that return value. To compensate for these two drawbacks of Runnable, one can use the remedy mentioned below - Callable.

Callable Interface #

Callable is an interface similar to Runnable. Classes that implement the Callable interface and classes that implement the Runnable interface can both be executed by other threads. Let’s take a look at the source code of Callable:

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

As we can see, it is also an interface, and the call method already declares throws Exception. It also has a generic return type V, which is a significant difference from Runnable. By implementing the Callable interface, we need to implement the call method, and the return value of this method is of type V. With this return value, we can obtain the result of the thread execution using the return value of the call method.

Differences between Callable and Runnable #

Finally, let’s summarize the differences between Callable and Runnable:

  • Method Name: Callable specifies the execution method as call(), while Runnable specifies it as run().
  • Return Value: Callable tasks have a return value, while Runnable tasks do not.
  • Exception Throwing: The call() method can throw exceptions, while the run() method cannot throw checked exceptions.
  • When working with Callable, there is a Future class that allows us to understand the task execution status, cancel the task execution, and obtain the task execution result. These functionalities cannot be achieved with Runnable. Callable has more powerful features compared to Runnable.

That’s all for this lesson. First, we introduced the two drawbacks of Runnable: the inability to return a value and the inability to throw checked exceptions. Then, we analyzed why these drawbacks exist and why Runnable is designed this way. We then discussed the Callable interface and compared it with the Runnable interface.