使用submit提交FutureTask的过程分析

FutureTask的使用

在使用FutureTask的过程中遇到过以下问题:

public class Problem {
    private volatile int num=1;
    public static void main(String[] args){
        ExecutorService service = Executors.newFixedThreadPool(2);//创建一个固定的线程池
        FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "sb";
            }
        });
        try {
            Future ss=service.submit(futureTask);
            System.out.println(futureTask.get());
            System.out.println(ss.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

该程序的输出结果是:

sb
null
//并且开始阻塞

这个地方的疑惑在于使用变量futureTask可以获取到运行结果,而使用变量ss却未能获取结果?因此去查阅了相关源码。

public class FutureTask<V> implements RunnableFuture<V>;
public interface RunnableFuture<V> extends Runnable, Future<V>;

由上面的代码可以看出FutureTask是实现了Runnable接口,并未实现Callale接口,因此是不会有返回结果的,所以使用ss获取不了结果,并且不应该这样使用。那为什么futureTask可以获取到结果呢?
在代码中,使用的是固定线程池来执行任务,用的是submit()来提交任务,submit()及其他部分的源码如下:

submit()函数会将所有的任务封装到FutureTask中,FutureTask中只包含一个变量private Callable<V> callable;,因此Runnable会被转换为Callable

//AbstractExecutorService类中的方法
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}
//FutureTask类中方法
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

//Excutors类中的方法和静态类部类
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}
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;
    }
}

submit()提交任务时使用静态函数newTaskFor()来生成了一个FutureTask实例,使用的构造方法如代码所示,构造方法再使用Excutors类中的方法callable生成了一个Excutors类中RunnableAdapter<T>类的实例。因此在上述Problem类中的futureTask变量被封装到了一个RunnableAdapter<T>的实例中,然后该实例作为ftask中的成员存在(this.callable = Executors.callable(runnable, result);)。然后再执行execute()方法,execute()方法实际调用的是ftask变量的run()方法,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();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    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);
        }
}

由代码可以看到run()方法执行的是callable实例的call()方法,在本例中ftaskcallable实例是RunnableAdapter<T>类的一个实例,该类的call()方法在上述代码中可以看到,在该方法中调用了futureTask变量的run方法,并且返回了一个结果nullnull是在submit()方法中构造一个FutureTask实例时传入的),并将null设置为ftask中实例域result的值,因此ftask的执行结果就是null,而futureTask变量执行run方法会执行自定义的那段代码,同时返回结果sb,并将该结果设置到futureTaskresult实例域。
这就是为什么使用ss变量获取到的是null,而使用futureTask获取到的是执行结果的原因。

posted @ 2018-03-27 15:10  baixiaoshuai  阅读(1102)  评论(0编辑  收藏  举报