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