[Java] When to use Runnable? When to use Callable ?

Article directory

  • foreword
  • Runnable
  • Callable
  • how to choose which one to use
  • at last

Foreword

When it comes to Java, you have to talk about multithreading. Even if you don’t want to talk about it, the interviewer has to ask you to talk about it, right? When it comes to multithreading, you don’t have to mention threads (isn’t this nonsense). When it comes to threads, you have to talk about the two guys Runnable and Callable.

Familiarity is really familiar. When I first learned multithreading, 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 Callable.
But it’s familiar, and the impression is not very big. It seems that I just used the names of these two, and the rest has nothing to do with them.
Today, let’s take a look at what these two are, what is the difference, when should Runnable be used, and when should Callable be used.

Runnable

Since the birth of Java, Runnable has existed, and the veteran among the veterans, before 1.5, if you want to use threads, you must implement Runnable. Callable was added to JDK only after JDK1.5.

@FunctionalInterface
public interface Runnable {<!-- -->
    public abstract void run();
}

Is the interface very simple, just an abstract method.
In fact, it can be simplified again. @FunctionalInterface indicates that this interface is a functional interface. The functional interface was only added in JDK8, in order to realize functional programming, just that kind of Lambada expression.
So in JDK1.7, there is no @FunctionalInterface decoration, simple and simple. We found the Ruunable implementation of JDK1.7, which is as follows

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 implementing the Runnable interface can be run by instantiating a Thread instance and passing itself as the target.
for example
First define a RunnableThread class, and implement the (implements) Runnable interface, and then rewrite the run method.

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

Use RunnableThread as the parameter of the 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 new Thread instance is called instead of the run method, although we rewrite the run method of Runnable. Calling the run method does not have the effect of creating a thread, but is executed directly in the current thread, just like executing a normal method of a normal class.

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

The main logic of this start0 method is to start an operating system thread, bind it to the JVM thread, open up some space to store thread state and context data, and then execute the bound JVM thread (that is, the class we implemented Runnable) The code block of the run method 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 is so perfect, there is 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 very convenient. It must be done with the help of shared variables.
Therefore, if your scenario requires a return value, Callable is required.

Callable

Callable was added in JDK1.5 to make up for the defect that Runnable has no return value, although most scenarios can be implemented with Runnable.

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

Similar to Runnable, @FunctionalInterface was added later, so it can be ignored, just for functional writing.
The Callable interface has only one call method, and has a generic return value, which can return any type.
for 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();
    }
}

Called 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 looks a little more complicated than Runnable, and FutureTask is needed, because the Thread class does not accept the Callable constructor.
Use the FutureTask.get() method to get the execution result.
In daily development, it is not recommended to use this directly, unless you clearly know that there is no problem with doing so, otherwise, it is recommended to use threads

The way to use the 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 you need a return value. If you don’t need a return value, just choose Runnable without hesitation. If there is a return value, then don’t hesitate, don’t think about using shared variables.

Another point is whether an exception needs to be thrown. Runnable does not accept throwing exceptions, and Callable can throw exceptions.
Runnable is suitable for pure asynchronous processing logic. For example, the report is calculated regularly every day, and the report is stored in the database or other places. It is only for calculation and does not need to be displayed immediately. The displayed content is obtained separately in other methods.

For example, for those non-core functions, after the core process is executed, the non-core functions can be executed by themselves. As for whether they are successful or not, it is not particularly important. For example, in a shopping order process, placing an order, reducing inventory, adding to the user’s order list, and debiting are the core functions. Afterwards, send APP notifications and SMS notifications to start a new thread to do it.

Last

Runnable is under the package java.lang, and when JDK1.5 was released, the newly added Callable was placed under the package java.util.concurrent, which is a well-known concurrent programming in Java Related packages, various locks, and multi-threaded tools are all placed under this package. It stands to reason that Runnable should also be here.

It can be seen that no matter how powerful the project is, it is slowly planned as the project expands, and some seemingly unreasonable places in the early stage can only be compatible and compromised.