Difference between Runnable and Callable in Java

Runnable has always been at the heart of multithreading in Java development, and Callable is an enhancement added to Java 1.5.

In this article we’ll explore the difference between Runnable and Callable in more detail.

Operation mechanism

Let’s look at the interface definitions for Runnable and Callable:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run(a);
}
Copy the code
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call(a) throws Exception;
}
Copy the code

Runnable needs to implement the run () method and Callable needs to implement the call () method.

We know that there are two ways to customize a Thread: one is to inherit the Thread, and the other is to implement the Runnable interface. This is because Thread itself is a Runnable implementation:

class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives(a);
    static{ registerNatives(); }...Copy the code

So Runnable can be executed using Runnable and the ExecutorService we described earlier, while Callable can only be executed with ExecutorService.

The difference in return values

As defined by the two interfaces above, Runnable does not return values, whereas Callable does.

If we all commit through the ExecutorService, see what the difference is:

  • Using runnable
    public void executeTask(a) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(()->log.info("in runnable!!!!"));
        executorService.shutdown();
    }
Copy the code
  • The use of callable
    public void executeTask(a) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(()->{
            log.info("in callable!!!!");
            return "callable";
        });
        executorService.shutdown();
    }
Copy the code

Although we both return the Future, the Future will not contain any values in the runnable case.

Exception handling

Runnable’s run () method definition does not throw any exceptions, so any Checked exceptions need to be handled in the run () implementation itself.

The Call () method of Callable throws a throws Exception, so you can catch a Checked Exception outside the Call () method. Let’s look at exception handling in Callable.

 public void executeTaskWithException(a){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(()->{
            log.info("in callable!!!!");
            throw new CustomerException("a customer Exception");
        });
        try {
            Object object= future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
            e.getCause();
        }
        executorService.shutdown();
    }
Copy the code

In the example above, we throw a custom CustomerException in the Callable.

This exception will be included in the returned Future. When future.get() is called, ExecutionException is thrown. At LLDB etCause(), specific exception information is retrieved.

Examples of this article can be found at github.com/ddean2009/l…

See flydean’s blog for more tutorials