Why can the submit of java thread pool submit both runnable and callable?

preface

Hello, everyone. Today, brother chicken wing continues to give you an in-depth understanding of multithreading. We all know that multithreading can be implemented in two ways: one is to implement runnable interface and the other is to implement callable interface. Both of the submit methods of the thread pool can be passed as parameters. You should know that submit is only used when the return value is required, and runnable has no return value. Why can submit receive two types? Let's continue to look.

submit method parsing

First, take a look at the submit method to make sure that you can submit in two ways.

We define a thread pool to execute two methods. The first has no return value, and the thread pool will automatically find the runnable interface. The second has a return value, and the thread pool will use the callable interface. Here, you can directly click submit to see the specific method.

    @Test
    public void test(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,5,5, TimeUnit.SECONDS,new LinkedBlockingDeque<>(1024));
        Future<?> submit = threadPoolExecutor.submit(() -> {
            System.out.println("submit.runnable.way");
        });
        Future<String> submit1 = threadPoolExecutor.submit(() -> {
            return "submit.callable.way";
        });
    }

We can find a phenomenon from above. Whether runnable or callable, the return value is future. Callable, we can understand that it returns a future. So runnable has no return value. How can it be converted to future. Let's keep watching

How does runnable become future?

Let's first look at the source code of the first paragraph

    public Future<?> submit(Runnable var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            RunnableFuture var2 = this.newTaskFor(var1, (Object)null);
            this.execute(var2);
            return var2;
        }
    }

We found that after the runnable is passed in, it is wrapped through the newTaskFor and returns the RunnableFuture, which inherits runnable and future. Futuretask implements this interface again, so you can naturally return futuretask.

FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

Continue to drill down to newTaskFor and find that it wraps runnable.

    protected <T> RunnableFuture<T> newTaskFor(Runnable var1, T var2) {
        return new FutureTask(var1, var2);
    }

Continue to track the construction parameters of futuretask. The discovery is that from this moment, your runnable becomes callable. Continue to drill down to the callable method.

    public FutureTask(Runnable var1, V var2) {
        this.callable = Executors.callable(var1, var2);
        this.state = 0;
    }
    public static <T> Callable<T> callable(Runnable var0, T var1) {
        if (var0 == null) {
            throw new NullPointerException();
        } else {
            return new Executors.RunnableAdapter(var0, var1);
        }
    }

There is an adapter for RunnableAdapter. Here we can see that the RunnableAdapter implements Callable. In this way, Callable is returned. When we call the call method of RunnableAdapter, we actually call the run method of runnable.

    private static final class RunnableAdapter<T> implements Callable<T> {
        private final Runnable task;
        private final T result;

        RunnableAdapter(Runnable var1, T var2) {
            this.task = var1;
            this.result = var2;
        }

        public T call() {
            this.task.run();
            return this.result;
        }

        public String toString() {
            return super.toString() + "[Wrapped task = " + this.task + "]";
        }
    }

At this point, runnable becomes callable.

Submit submit callable

After understanding the runnable above, it is very simple to understand the callable. Let's take a look at his source code. We can see that it is directly encapsulated into futuretask. There is no need to convert runnable as above.

    public <T> Future<T> submit(Callable<T> var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            RunnableFuture var2 = this.newTaskFor(var1);
            this.execute(var2);
            return var2;
        }
    }
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> var1) {
        return new FutureTask(var1);
    }
    public FutureTask(Callable<V> var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            this.callable = var1;
            this.state = 0;
        }
    }

ending

There are inevitable deficiencies in the article. Welcome to discuss! At the same time, you can talk to brother chicken wings.

 

Keywords: Java Back-end

Added by cyberdwarf on Thu, 06 Jan 2022 00:26:55 +0200