When to use Runnable? When to use Callable?

When it comes to Java, we have to talk about multi-threading. Even if you don’t want to say it, the interviewer has to let you say it, right? When it comes to multi-threading, threads are not allowed to be mentioned (isn’t this nonsense). When it comes to threads, we have to talk about Runnable and Callable.

To say that it is familiar is to say that it is really familiar. When I first learned multi-threading, the first example was probably as follows.

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Execution Thread" + Thread.currentThread().getName());
    }
}).start();

I saw Runnable, and sometimes I saw Callable.

But I’d say it’s very familiar, and the impression isn’t very big. It seems like I just used the names of these two people, and the rest has nothing to do with them.

Today, let’s take a look at what these two are, what are the differences, when should we use Runnable, and when should we use Callable.

Runnable

Since the birth of Java, Runnable has existed, the oldest among the elders. Before 1.5, if you wanted to use threads, you had to implement Runnable. Because with JDK1.5, JDK added Callable.

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

Is the interface very simple, just an abstract method?

In fact, it can be simplified further. @FunctionalInterface indicates that this interface is a functional interface. The functional interface was only added in JDK8 to implement functional programming, like Lambada expressions.

So in JDK1.7, there is no @FunctionalInterface modification, it’s simple. We found the Ruunable implementation of JDK1.7, which looks like this

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

If a thread class implements the Runnable interface, the class must define a parameterless method named run.

A class that implements the Runnable interface can be run by instantiating a Thread instance and passing itself as the target.

Give an example

First define a RunnableThread class, implement the Runnable interface, and then override the run method.

public class RunnableThread implements Runnable{
    @Override
    public void run() {
        System.out.println("Current thread name" + Thread.currentThread().getName());
    }
}

Use RunnableThread as a parameter for thread class (Thread) instantiation, and then call the run method.

RunnableThread runnableThread = new RunnableThread();
Thread thread = new Thread(runnableThread);
thread.start();

Note that the start() method of the newly created Thread instance is called. Do not call the run method, although we are overriding Runnablerun method. Calling the run method does not have the effect of creating a thread, but is executed directly on the current thread, just like executing an ordinary method of an ordinary class.

Why should we call the start() method? Let’s take a look at the implementation of the start() method of Thread. In fact, a method named Thread is called. The native method of code>start0() is not implemented in Java, but is implemented at the JVM level.

The main logic of this start0 method is to start an operating system thread and bind it to the JVM thread, open up some space to store thread status and context data, and then execute the bound JVM thread (that is, our The code block that implements the run method of the Runnable class) to execute our custom logic.

You can also use the thread pool to call

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("thread-pool-%d").build();
ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

singleThreadPool.execute(runnableThread);
singleThreadPool.shutdown();

If Runnable was so perfect, there would be no need to add Callable which is super similar to it in JDK1.5.

Is there anything imperfect about Runnable? That is, its run method has no return value.

If you want to get the return value of the newly opened thread in the main thread, Runnable is not convenient. This must be done with the help of shared variables.

Therefore, if your scenario requires a return value, Callable should be used.

Callable

Callable was added in JDK1.5 to make up for the defect of Runnable not returning a value, although Runnable can be used in most scenarios > to achieve.

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

Similar to Runnable, @FunctionalInterface was also added later and can be ignored, just for functional writing.

The Callable interface has only one call method and a generic return value that can return any type.

Give an example

First declare a class that implements the Callable interface and returns a string type

public class CallableThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Thread name:" + Thread.currentThread().getName();
    }
}

Call it in the code in the following way

CallableThread callableThread = new CallableThread();
FutureTask<String> futureTask = new FutureTask<>(callableThread);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println("Execution result = " + result);

It seems a little more complicated than Runnable, and you need to use FutureTask, because the Thread class does not accept the constructor of Callable function.

Use the FutureTask.get() method to obtain the execution result.

In daily development, it is not recommended to use it directly like this, unless you clearly know that there is no problem in doing so. Otherwise, it is recommended to use the thread pool.

CallableThread callableThread = new CallableThread();
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callableThread);
String result = future.get();
System.out.println("Task execution result: " + result);
executor.shutdown();

How to choose which one to use

The basic principle of choice is whether a return value is required. If a return value is not required, just choose Runnable without hesitation. If there is a return value, don’t hesitate and don’t think about using shared variables.

Another point is whether exceptions need to be thrown. Runnable does not accept exceptions, but Callable can throw exceptions.

Runnable is suitable for purely asynchronous processing logic. For example, a report is calculated regularly every day, and the report is stored in a database or other places. It only needs to be calculated and does not need to be displayed immediately. The display content is obtained separately in other methods.

For example, for those non-core functions, after the core process is completed, the non-core functions can be executed by themselves. Whether they are successful or not is not particularly important. For example, in a shopping order process, placing orders, reducing inventory, adding to the user’s order list, and deducting money are the core functions. After that, just start a new thread to send APP notifications and SMS notifications.

Finally

Runnable is under the package java.lang, and when JDK1.5 was released, the newly added Callable was placed in Under the package java.util.concurrent, this is a famous concurrent programming related package in Java. Various locks and multi-threading tool classes are all placed under this package. Logically, Runnable should also be here.

Recommended reading:

Precision assault! Mysql billion-level data development manual, GitHub 132k starts – Practical analysis – Toutiao (toutiao.com)

The principle of concurrency optimistic locking CAS, ask the interviewer about concurrency (skip it if you have no basic knowledge) – Toutiao (toutiao.com)

Dominating the GitHub weekly list! Java interview gospel, force yourself to memorize it in a week and land in a big company!

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 138,186 people are learning the system