为什么java线程池的submit既可以提交runnable也可以提交callable?

前言

大家好,今天鸡翅老哥继续带大家深入了解多线程,我们都知道多线程有两种实现方式,一种是实现runnable接口,一种是实现callable接口。那么线程池的submit提交方式,两种都可以作为参数传递。要知道submit是当需要返回值的情况下才使用,runnable是没有返回值,那么submit为什么可以接收两种呢,我们继续往下看。

submit方法解析

先看下submit的方法,来确定,确实可以提交两种方式。

我们定义一个线程池,来执行两个方法,第一种没有返回值,线程池会自动找到runnable接口,第二种有返回值,线程池会走callable接口的方式。这里大家可以直接点击submit进去,就可以看到具体走的哪个方法。

    @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";
        });
    }

我们从上面可以发现一个现象。无论是runnable还是callable,返回值都是future。callable我们可以理解,他就是返回一个future。那么runnable是没有返回值的,是怎么转换成future的呢。我们继续看

runnable如何转为future的?

先来看第一段源码

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

我们发现runnable传进去后,通过newTaskFor进行了包装,返回了RunnableFuture,RunnableFuture 又继承了runnable和future。futuretask又实现了这个接口,所以自然就可以返回futuretask了。

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

继续深入到newTaskFor,发现他对runnable进行了包装。

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

继续跟踪,futuretask的构造参数。发现就是从这一刻开始,你的runnable变为了callable。继续深入到callable方法。

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

这里面有一个RunnableAdapter的适配器。这里我们可以看到RunnableAdapter实现了Callable。这样返回的时候就是Callable,当我们调用RunnableAdapter的call方法的时候,实际调用的就是runnable的run方法。

    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 + "]";
        }
    }

至此,runnable就转为callable了。

submit提交callable

了解了上面的runnable,再来理解callable就十分简单了。我们看一下他的源码。我们可以看到,直接封装到futuretask。不需要像上面一样进行runnable的转换。

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

结尾

文中难免有不足了,欢迎大家讨论!同时有问题,可以找鸡翅老哥交流哦。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值