源码分析下为什么线程池的submit()可以接收返回值

先看看submit()方法的源码:

 public <T> Future<T> submit(Callable<T> task) {
        //空值判断
        if (task == null) throw new NullPointerException();
        //设置构造一个RunnableFuture对象
        RunnableFuture<T> ftask = newTaskFor(task);
        //调用execute()方法
        execute(ftask);
        //返回Future对象
        return ftask;
    }

接着看看newTaskFor()方法是怎么为我们构造出RunnableFuture对象的

//可以发现最终返回的其实是RunnableFuture对象的子类FutureTask对象
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

继续看看FutureTask类的构造方法

public class FutureTask<V> implements RunnableFuture<V> {
  public FutureTask(Callable<V> callable) {
       //空值判断
        if (callable == null)
            throw new NullPointerException();
        //设值    
        this.callable = callable;
        //设值,这其实表示的就是线程任务的一种状态,这里表示新建
        this.state = NEW;       // ensure visibility of callable
    }
}

想一想,既是然是线程任务,那么肯定是需要实现Runnable和重写run()方法的

带着这个思路,回头看看RunnableFuture类的源码

//发现RunnableFuture类继承了Runnable类和Future类,但是因为是接口,所以其实没有具体实现run()方法
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

既然RunnableFuture是接口没有实现run()方法,而我们构造的又是它的子类FutureTask,所以可想而知,只要FutureTask想做点事,那么它就肯定会重写run()方法的

所以看看FutureTask类的run()方法源码:

public class FutureTask<V> implements RunnableFuture<V> {

 //Callable对像
 private Callable<V> callable;
 //用来接收返回值
 private Object outcome;
 
 public void run() {
        //做个状态判断
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            //可以发现,这个callable其实在构造FutureTask对象时,已经作为参数设置成FutureTask的属性了
            Callable<V> c = callable;
            //做个判断
            if (c != null && state == NEW) {
                //定义一个泛型来接受返回结果
                V result;
                //用这个值来判断线程运行时是否发生了异常
                boolean ran;
                try {
                    //可以发现其实是调用了Callable对象的call方法,
                    //按照我们初学线程的知识,想要创建有返回值的线程任务的话,就使用Callable,
                    //这里就不具体深入Callable了,只需知道最后其实实际还是调用了Callable对象的方法才取到的返回值
                    result = c.call();
                    //运行成功,没有异常,所以设置成true
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    //运行异常,所以设置成false
                    ran = false;
                    setException(ex);
                }
                //如果运行正常
                if (ran)
                    //这步其实是把返回值设置到FutureTask类中的outcome属性中
                    set(result);
            }
        } 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);
        }
    }
}

看看上面set(result)方法做了什么:

protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //把返回值设置到FutureTask类中的outcome属性中
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

完成了以上步骤的分析后,最后在看看Future.get()又是怎么拿到值的

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            //这里可以大致理解为做个等待,等待线程任务执行完成
            s = awaitDone(false, 0L);
        //调用report() 方法
        return report(s);
    }

跟进report(s)方法里

private V report(int s) throws ExecutionException {
        //把outcome属性的值赋值给x ,这个outcome属性的值就是返回值,之前刚赋值过
        Object x = outcome;
        //如果线程正常运行 
        if (s == NORMAL)
            //就返回值
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

走到这步,就会发现get方法除了一些判断外,其实最后要获取的就是outcome属性的值来作为返回值。

扫描二维码关注公众号,回复: 11144890 查看本文章

总结

其实可以发现线程池的submit()方法底层还是依靠着Callable线程对象的方法才能拿到返回值,然后把这个值赋值到属性里,获取的时候就返回这个属性的值。

原创文章 358 获赞 387 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_38106322/article/details/105574267