“This is the 21st day of my participation in the First Challenge 2022. For details: First Challenge 2022”

JDK version: JDK11

1. The background

Runnable: Runnable: Runnable: Runnable: Runnable

public class ThreadTest {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println(111); }},"Thread-mxsm").start(); }}Copy the code

Create a new thread object and execute a Runnable to finish the thread.

In addition to this there is the use of thread pools, as follows:

public class ThreadTest {

    public static void main(String[] args) {
       ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new Runnable() {
            @Override
            public void run(a) {
                System.out.println(111); }}); }}Copy the code

But in the thread pool to perform the commit task, you might notice something like this:

At position 1, you’ll notice that you can actually submit a Callable to the thread pool for execution.

Here’s the question:

  • What is the relationship between Runnable and Callable, and why thread pools can submit Callable.
  • From a single thread perspective, thread Thead can only execute implementations of the Runnable interface, but why should a thread pool execute Callable
  • How does a Callable get wrapped up as Runnable for a thread to call

2. Association analysis of Callable and Runnable

Analyze the relationship between the two from the three aspects listed above.

2.1 Callable and Runnable source code analysis

Runnable Runnable Runnable Runnable Runnable

@FunctionalInterface
public interface Runnable {
    public abstract void run(a);
}
Copy the code

There is only one method, run. Any class that needs to be executed by Thread needs to implement Runnable.

So Callable should be neatly converted to Runnable

Callable is not used much, look at the source code:

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

Callable also has only one method called but that method returns a value.

Callable and Runnable do not have any inheritance relationship, Runnable method does not return value, and Callable method return value.

2.2 How to wrap Callable as Runnable from thread pool

The JDK makes it clear in the comment on Runnable that any class that needs to be executed by Thread needs to implement Runnable. So we can infer that Callable is somehow wrapped as Runnable.

Submit the Callable with the ExecutorService#submit method to analyze:

  1. The AbstractExecutorService#submit newTaskFor method converts a Callable to a RunnableFuture.

As you can see from the newTaskFor method, you end up creating a FutureTask object with Callable as the constructor argument. So look at FutureTask:

public class FutureTask<V> implements RunnableFuture<V> {
    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
    
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
	// Omit some code
}
Copy the code

Callable is used as a property value for FutureTask. As mentioned earlier, Runnable must be implemented in order to be executed by Thread. So let’s look at FutureTask’s implementation class, RunnableFuture:

  1. RunnableFuture inherits Runnable

In other words: FutureTask implements Runnable

  1. FutureTask#run internally calls the Callable call method

Threads in the Thread pool then execute FutureTask(that is, an instance of Runnable’s implementation). The above class inherits from:

Callable to Runnable process:

  1. Developers implement the Callable interface
  2. Instantiate the Callable and commit to the thread pool
  3. Create FutureTask with Callable as constructor
  4. Eventually FutureTask is submitted to the thread in the thread pool for execution

Tips: When submitting an implementation of Runnable to the thread pool for execution, the Runnable instance is adapted to a Callable using the RunnableAdapter adapter if the return value is required.

 private static final class RunnableAdapter<T> implements Callable<T> {
     private final Runnable task;
     private final T result;
     RunnableAdapter(Runnable task, T result) {
         this.task = task;
         this.result = result;
     }
     public T call(a) {
         task.run();
         return result;
     }
     public String toString(a) {
         return super.toString() + "[Wrapped task = " + task + "]"; }}Copy the code

2.3 What are the features of Callable execution by thread pool

When a Callable is wrapped as a Runnable, the thread pool performs the same as a Runnable, with the Callable being able to retrieve the return value. If the executing logic doesn’t care about the return value, you can just use Runnable. But if you need to get a return value into the business logic, then you use Callable to commit to the thread pool.

3. Summary

  • There is no inheritance relationship between Runnable and Callable; Callable is wrapped as Runnable by FutureTask.
  • When a thread pool performs a task, it uses Callable if the relation returns a value, and Runnable if the relation returns a value.
  • Runnable If return values are also required, the thread pool internally ADAPTS to Callable via the RunnableAdapter adapter

I am ant back elephant, the article is helpful to you like to pay attention to me, the article has incorrect place please be corrected to leave a comment ~ thank you!