submit()和execute()区别原理 通过源代码的区别

之前的一篇线程池 submit()执行怎么获取到的返回值只是讲了怎么使用,还有一部分源码。

这篇具体从 提交任务,到具体怎么 传入Runnable 会可以有返回值?并且还涉及线程池中线程怎么执行任务?

概念级的区别可以看之前的文章。

  1. 从submit构造方法开始
    submit()的出现在ExecutorService,具体实现是它的实现类AbstractExecutorService
    有三个构造方法
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    第一个就直接使用Callable。
    之后两个的区别,我认为只是有无泛型的区别。
    
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
    
     public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
    主要调用就是这个方法
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    	//直接new了一个futureTask
        return new FutureTask<T>(runnable, value);
    }
    
    这三个方法体几乎一模一样。那么具体的区别在哪里?前提先了解一下RunnableFutureFutureTask
    
    RunnableFuture
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }
    实现了RunnableFuture。
    子类为FutureTask
    public class FutureTask<V> implements RunnableFuture<V> 
    
    那么就明白了,RunnableCallable并且有返回值,主要就应该在这个FutureTask中体现。
    
    FutureTask
    它一共有两个构造方法
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    认真看一下就发现了
    传入Callable直接赋值给类的成员变量
    传入Runnable则是执行了一共方法,看一下这个方法,具体转化逻辑就有了
    
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
    一看又new了一个类。。。
    
    RunnableAdapter
    这个是一个静态内部类。很短
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }
    可以看到实现了Callable接口。并且在call()方法中,调用了task的run()。也就是相当于任务代码直接在call()方法中。
    
    明白了Runnable是通过一个静态内部类 被转化为Callable.并且执行完call()方法也会返回result。也可以看出,之前说我认为只是为了泛型的由来了。
    
  2. 接下来说一下线程池中线程是怎么执行任务的?
    不说全部的代码,只说涉及到这部分的代码
    在线程池的实现类ThreadPoolExecutor中线程执行任务的具体方法是runWorker() Worker是一个封装了线程的类。
    这个方法中,会自旋获取任务。while (task != null || (task = getTask()) != null)。
    如果是核心线程则不会从阻塞队列中获取任务。否则都会从阻塞队列中getTask()
    在这个方法中,会task.run()这个run方法则是RunnableFuture中的run()方法.
    具体实现在子类FutureTask
    FutureTask.run()
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();//从这里可以看到,调用了call()方法
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);//这里将返回值设置到了outcome,然后通过get()获取
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    //复杂,是因为阻塞获取
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    private V report(int s) throws ExecutionException {
        Object x = outcome;//result
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
    具体的流程:
    线程获取任务执行 调用FutureTask.run() 调用Callable.call() 调用Runnable.run() 
    区别在于submit传入的是Callable,那么不用封装。传入的是Runnable则需要封装为Callable,通过一个实现了Callable接口的中间类RunnableAdpter
    

总结:源码中体现的区别,就在于要不要封装为RunnableAdpter。其余都一样。

还有一个注意的点,submit()提交的任务抛出了异常,不会被打印,而会被捕获到回调结果中,也就是get()返回的是异常结果
execute()则会打印到控制台

				try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
抛出异常会被捕获
protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }set()一样。

线程抛出了异常,这个线程会被从线程哈希表中移除,取消强引用,让GC回收,并且重新创建一个新的线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值