Java创建线程 为什么Callable需要FutureTask包装一下?
1、首先通过Callable
接口创建线程的Demo如下:
//1、定义一个类MyCallable实现Callable接口重写call方法,泛型为返回值类型
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建MyCallable类的实例
MyCallable myCallable = new MyCallable();
//通过FutureTask包装MyCallable类的实例,泛型为返回值类型
FutureTask<Integer> f = new FutureTask<Integer>(myCallable);
//通过new Thread的方式,创建线程,并启动线程
new Thread(f).start();
//通过FutureTask的对象f获得最终线程执行后的返回值,该返回值可用于另一线程,这里可以用于主线程
System.out.println(f.get());// 最终结果输入1
}
}
2、查看接口Callable
的源码
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
通过上面源码可以发现,Callable
接口有一个泛型作为返回值,除此之外没有任何继承或者实现关系
3、查看FutureTask
类的构造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
通过上面源码可以发现,Callable
接口可以构造FutureTask
类的对象
3、查看FutureTask
类的结构
从该结构可以看出,FutureTask
是一个类(抽象类),因此FutureTask
会实现Runnable接口
4、查看Thread
类的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
从该构造方法可以看出,参数是一个Runnable
,那么可以分析得到:
-
首先可以通过
FutureTask
的构造方法,传入Callable
接口的实例,构造FutureTask
对象 -
由于
FutureTask
间接实现了Runnable
接口,同时Thread
类的构造方法要求放入一个Runnable
,这时候就可以放入当前构造的FutureTask
对象 -
最后通过
Thread
的start()
方法开启一个线程
最后再次贴上文章开头的代码,结合上面的分析,方便理解:
//创建MyCallable类的实例
MyCallable myCallable = new MyCallable();
//通过FutureTask包装MyCallable类的实例,泛型为返回值类型
FutureTask<Integer> f = new FutureTask<Integer>(myCallable);
//通过new Thread的方式,创建线程,并启动线程
new Thread(f).start();
//通过FutureTask的对象f获得最终线程执行后的返回值,该返回值可用于另一线程,这里可以用于主线程
System.out.println(f.get());// 最终结果输入1