从源码剖析FutureTask、RunnableFuture、Future、Runnable、Callable的关系
首先我们来看一下这几个类之间的关系依赖图:
现在开始分析源码,从FutureTask类开始,这个类由于实现了RunnableFuture接口,而该接口又继承了Runnable接口和Future接口,所以这个类实现的方法主要如下:run、cancel、isCancelled、isDone、get。前面一个是用来供线程调用,后面四个用来获取线程的执行结果。我们看看它的构造函数,主要有两个:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
从这两个构造函数可以看出,该类可以传入Callable和Runnable类实现的对象,但是不管传入的是哪个类实现对象,都最终会转换成Callable类实现对象,最终线程调用的函数是Callabe中的call函数,也就是说如果传入的是Runnable类实现对象,那么就会将该对象转换成Callable类实现对象,这个转换操作就是通过Executors.callable函数来实现的,我们进入到这个函数看看:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
下面我们看看RunnableAdapter<T>的实现代码:
/**
* A callable that runs given task and returns given 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;
}
}
这个类会将Runnable类实现对象和result存储到相应的类成员变量中,同时实现了一个call函数,这个函数中执行了Runnable类实现对象的run函数并返回result。流程图如下图所示:
最终如果传入的是Runnable实现类对象,也会转换成Callable。接下来我们来看看示例:
public class Test {
public static void main(String[] args) {
//第一种方式
/*ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
executor.submit(futureTask);
executor.shutdown();*/
//第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
System.out.println("task运行结果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
}
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
}
执行结果如下:
子线程在进行计算
主线程在执行任务
task运行结果4950
所有任务执行完毕