与多线程相关的方法—Callable,Future以及FutureTask
除了上文中一直分析的Runnable之外,Java中还存在Callbale,Future,FutureTask与多线程相关的概念,与Runnable不同的则是这三个方法只能用于线程池中,Runnable则可以同时在运用在Thread和线程池中。
Callable与Runnable功能相似,不同在与Callable是一个泛型接口,他有一个泛型参数V,该接口中有一个返回值(类型为V)的call()函数,而Runnable的run函数不能将结果返回到客户程序中:
public interface Callable<V>{
//返回V类型的结果
V call() throws Exception;
}
//为加深理解,假如Runnable的源码作为对比
public interface Runnable{
//返回V类型的结果
public abstract void run();
}
博客看到此处,即对Runnable以及Callable有了比较大致的了解,但是这两种机制存在着一个无法避免的缺陷,即一旦使用后就很难做到有效的控制。而Future的出现即解决了这种问题,Future为线程池制订了可管理的任务标准。Future提供了对Runnable或者Callable的任务的执行结果进行管理(取消,查询是否完成,获取结果,设置结果;分别对应着cancel,isDone,get,set函数,且get函数调用后会发生阻塞,直到执行完成返回结果)。Future声明如下:
public interface Future<V>{
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException,ExecutionException;
V get(long timeout,TimeUint unit)
throws InterruptedException,ExecutionException,TimeoutException;
}
Future只是定义了一些规范的接口,FutureTask则是具体的实现类。FutureTask实现了RunnableFuture,而RunnableFuture实现了Runnable又实现了Future这两个接口,因此FutureTask同时具有两者的能力。FutureTask代码如下:
public class FutureTask<V> implements RunnableFuture<V>{
//代码省略
}
再看RunnabelFuture类定义:
public class RunnabelFuture<V> implements Runnable,Future<V>{
void run();
}
FutureTask会像Thread包装Runnable那样对Runnable和Callbale进行包装,Runnbale与Callbale由构造函数注入。
public FutureTask(Callable<V> callable){
if(callable==null) throws new NullPointerException();
this.callable=callable;
this.state=NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable,V result){
this.callable=Executors.callable(runnable,result);
this.state=NEW; // ensure visibility of callable
}
由上述代码即可看出,无论在构造函数中传输runnable或callable对象,都会被转换为callable对象,即可知道最终都是对Callable对象进行操作,该适配函数的实现如下:
public static <T> Callable<T> callable(Runnable task,T result){
if(task==null) throws 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;
}
}
由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可提交给ExecuteService来执行。且可直接通过get()获取执行结果,该函数会一直阻塞到结果返回。因此,FutureTask既是Future,Runnable,又包装了Callable,即为两者的合体
下面通过一个Java程序简单演示Runnable,Callable,FutureTask的运用:
public class FutureDemo {
static ExecutorService mExecutor=Executors.newSingleThreadExecutor();
public static void main(String[] args){
try {
FutureWithRunnable();
FutureWithCallable();
FutureWithFutureTask();
} catch (Exception e) {
// TODO: handle exception
}
}
private static void FutureWithRunnable() throws InterruptedException,ExecutionException{
Future<?> result=mExecutor.submit(new Runnable() {
public void run() {
// TODO Auto-generated method stub
fibc(20);
}
});
System.out.println("Future result from Runnable : "+result.get());
}
private static void FutureWithCallable() throws InterruptedException,ExecutionException{
Future<Integer> result2=mExecutor.submit(new Callable<Integer>(){
public Integer call() throws Exception {
// TODO Auto-generated method stub
return fibc(20);
}
});
System.out.println("Future result from Callable : "+result2.get());
}
private static void FutureWithFutureTask() throws InterruptedException,ExecutionException{
FutureTask<Integer> futuretask=new FutureTask<Integer>(
new Callable<Integer>() {
public Integer call() throws Exception {
return fibc(20);
}
});
mExecutor.submit(futuretask);
System.out.println("Future result from FutureTask : "+futuretask.get());
}
private static int fibc(int num){
if(num == 0)
return 0;
if(num == 1)
return 1;
return fibc(num-1)+fibc(num-2);
}
}
运行结果为:
Future result from Runnable : null
Future result from Callable : 6765
Future result from FutureTask : 6765
由上述示例代码以及结果可以看出,以Runnable作为对象的执行后的结果无法通过get()函数方法获得。而使用Callable以及调用Callable的FutureTask可以通过get()函数获取执行后的结果。