Future,FutureTask和Callabe关系及使用详解
文章目录
1.Future担当的角色
在介绍Future及其实现类FutureTask之前,我们首先要知道Future是做什么用的,为什么要设计这一系列的接口和实现类。
大多数的并发程序都是围绕“任务执行”来构造的,任务通常是一些抽象的且离散的工作单元。在设计并发程序时,将线程和任务解耦,可以使程序有更好的性能和伸缩性。举个简单的例子,在一个web程序中,可以将每一个用户请求当做是一个任务,每个请求根据不同的任务执行策略处理。
Future可以封装各种抽象的任务单元,不仅如此,它提供获取任务执行结果的接口get()
和其他非常实用的接口方法,比如取消任务,限时任务等,
2. FutureTask使用场景
FutureTask是一个可以返回任务执行结果的任务单元,通常在需要任务执行结果的场景中使用。
- 需要计算并且要获得计算值的场景
- 在搜索引擎中,在规定时间内获取搜索的结果,并将搜索结果返回给用户。(超时的任务将取消)
3. Callable、Future和FutureTask
Callabe接口
public interface Callable<V> {
V call() throws Exception;
}
Callable接口比较简单,只有一个方法,用来返回任务执行结果。
Future接口
Future接口定义了一系列任务执行的方法。
public interface Future<V>{
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException;
}
-
cancel(boolean mayInterruptIfRunning)
取消当前任务的执行。- 如果执行
cancel
时任务已完成或者之前已进行取消操作或者因为某些原因不能进行取消操作,那么将返回false
- 如果执行
cancel
时线程还未执行该任务,那么该任务将不会被执行,返回ture
- 如果执行
cancel
时线程正在处理该任务,且mayInterruptIfRunning
为false
,那么任务会继续执行到执行完成,此时返回ture
- 如果执行
cancel
时线程正在处理该任务,且mayInterruptIfRunning
为ture
,那么会中断该线程,此时返回ture
- 如果执行
-
isCancelled()
获取任务是否被取消。- 如果任务在取消前正常完成,那么返回
ture
- 如果任务在取消前正常完成,那么返回
-
isDone()
获取任务是否已完成- 如果任务已完成,返回
true
。如果任务时因中断,异常等原因被终止,也返回true
- 如果任务已完成,返回
-
get()
获取任务执行结果,get()
方法会一直阻塞直到任务完成。如果任务被中断,将抛出InterruptedException
-
get(long timeout, TimeUnit unit)
在规定时间内获取任务执行结果,如果没有在规定时间内完成任务则抛出TimeoutException
FutureTask类
以下为FutureTask的类图
FutureTask是Future接口的实现类也是唯一的实现类
,FutureTask实现了RunnableFuture,即同时实现了Future接口及Runnable接口,可以很方便的在线程中被执行。
FutureTask实现了Future和Runnable的接口,这里不再赘述。
不过有一点需要注意的是FutureTask的构造方法和实现run()的方法
public class FutureTask<V> implements RunnableFuture<V> {
private Callable<V> callable; //任务执行的主体
private volatile int state; //任务执行的状态
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
public run(){
/**省略部分代码**/
V result = callable.call();
/**省略部分代码**/
}
}
可以看到在FutureTask中,真正执行的方法是其成员变量Callable,这也是为什么FutureTask和Callable一起被使用的原因
4.FutureTask使用例子
简单的使用
这里定义一个简单的计算任务,将该任务放入一个新建立的线程中运行,然后调用get
方法获取计算的结果并输出。
public class useFutureTask(){
public static void main(String[] args){
//建立一个简单的计算任务
Future<Integer> future =new FutureTask<>(()->{
Integer a=100;
Integer b=1000;
return a+b;
});
//将该任务放入线程中运行
new Thread((FutureTask)future).start();
//获取任务计算结果
System.out.println(future.get());)
}
}
输出:1100
将任务放入线程池中执行
public class useFutureTask(){
public static void main(String[] args){
//建立一个简单的计算任务
Future<Integer> future =new FutureTask<>(()->{
Integer a=100;
Integer b=1000;
return a+b;
});
//新建一个带缓存的线程池,并将任务放入线程池中执行
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit((FutureTask)future);
//获取任务计算结果
System.out.println(future.get());
//记得关闭线程池,不然jvm虚拟机不会退出
executor.shutdown();
}
}
输出:1100