When to use Runnable and 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();

When programming in java, sometimes you will see Runnable, and sometimes you will see Callable. What exactly are these two, what are the differences, and when should you use Runnable, when should you use Callable. Let’s take a look today

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();
}

@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.

For 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());
    }
}

The startup code is as follows:

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

Callable

Callable was only added in JDK1.5 to make up for the defect of Runnable not returning a value.

@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.

//Callable example
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("This is a Callable task.");
        return 123; // Return an integer
    }
}

The calling code is as follows:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // Get the result of task execution

The difference between Runnable and Callable

Runnable and Callable are both interfaces used for multi-threaded programming in Java. The main differences between them are:

  1. Return value: The run() method of Runnable has no return value, while the call() method of Callable has one. Return value of generic type.

  2. Exception throwing: The run() method of Runnable cannot throw an exception, and the call() method of Callable Exceptions can be thrown. You need to use the try-catch statement or throw the exception upward.

  3. Usage: Runnable is usually used as a parameter to start a thread, while Callable is usually used as a parameter to submit a task to the thread pool, so that the results of task execution can be obtained.

Runnable and Callable usage notes

When using Runnable and Callable, you need to pay attention to the following matters:

  1. Return value processing: Runnable‘s run() method does not have a return value, but Callable‘s call() method does A return value of a generic type. For Callable tasks, the results of task execution can be obtained through the Future object. When getting results, you can choose to wait for task execution to complete (call future.get()) or set a timeout (call future.get(timeout, unit)).

  2. Exception handling: The run() method of Runnable cannot throw an exception, but the call() method of Callable can throw an exception. When using Callable, you need to pay attention to catching exceptions that may be thrown and handle them appropriately.

  3. Thread pool usage: Normally, Runnable tasks are suitable to be started using Thread, while Callable tasks are suitable to be submitted using the thread pool. Thread pools can better manage and reuse thread resources and improve program performance and efficiency.

  4. Task cancellation: You can cancel the task being executed through the cancel() method of the Future object. However, it should be noted that if the task has already started execution, it may not be aborted immediately. Additionally, if the task has been completed or canceled, calling the cancel() method again will return false.

  5. Concurrency safety: When using Runnable and Callable in a multi-threaded environment, you need to pay attention to the concurrency safety of the data. To ensure the correctness of shared data access, you can use synchronization mechanisms (such as synchronized keyword, Lock interface) or use concurrent containers (such as ConcurrentHashMap, CopyOnWriteArrayList, etc.) to ensure thread safety.

  6. Inter-thread communication: Runnable and Callable tasks can interact through shared variables or inter-thread communication mechanisms. For example, you can use the wait() and notifyAll() methods to implement the waiting and waking up mechanism.

How to choose between Runnable and Callable

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, then don’t hesitate and don’t think about using shared variables. Runnable does not accept throwing 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, you can start a new thread to send APP notifications and SMS notifications.

For more news information, please visit Angyan Data (https://www.ayshuju.com)