简介
Future
模式指的是专门分配一个可以执行取消操作的线程去处理耗时的任务,且可以返回处理结果。简单来说就是可以看做是一个带有返回值且可以取消的Thread
。
如果要了解Future
模式最好了解以下的类或接口:
Callable
:Callable
是一个接口,它可以看做是带返回值的Runnable
Future
:Future
也是一个接口,它定义了对Future模式做的线程可以执行的操作。RunnableFuture
:一个继承了Runnable
和Future
的接口,这个接口了表明Future
模式实际上就是一个特殊的线程。FutureTask
:RunnableFuture
的实现类,Future
模式的基本实现,单独使用其实是单线程的,需要配合线程池使用。ThreadPoolExecutor
:JDK
原生的线程池类。Executors
:线程池相关的工具类,可以用这个类快速构建线程池。
实例
既然介绍完Future
模式,下面就通过一个简单的实例去学习一下Future
模式的使用:
基本使用
首先是实现Callable
接口:
/**
* 通过休眠模拟耗时任务的Callable实现类,返回休眠耗时
* @author RJH
* @date 2018年1月31日
*/
public class LongTimeTask implements Callable<Long>{
/**
* 休眠时间
*/
private long sleepTime;
public LongTimeTask(long sleepTime) {
this.sleepTime = sleepTime;
}
@Override
public Long call() throws Exception {
Thread.sleep(sleepTime);
return sleepTime;
}
}
然后是使用Executors
构建线程池,并调用submit
方法执行FutureTask
:
/**
* Future模式的基本例子
* @author RJH
* @date 2018年1月31日
*/
public class FutureDemo {
public static void main(String[] args) {
//任务耗时
long sleepTime=1000;
//构建随机休眠的Callable对象
Callable<Long> task=new LongTimeTask(sleepTime);
//构建线程池
ExecutorService executorService=Executors.newSingleThreadExecutor();
//记录起始时间
long startTime=System.currentTimeMillis();
//分配线程执行Callable任务,并返回Future对象
Future<Long> future=executorService.submit(task);
try {
//主线程休眠任务耗时所需的时间
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//输出任务耗时
System.out.println("Future UsedTime:"+future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
//输出主线程运行时间
System.out.println("Main UsedTime:"+(System.currentTimeMillis()-startTime));
//关闭线程池
executorService.shutdown();
}
}
输出结果
Future UsedTime:1000
Main UsedTime:1002
分析
Future
模式需要构建线程池后,分配线程去执行对应的Callable
对象中的call方法。
而实际上使用FutureTask
来处理实际的任务,FutureTask
其实基本的使用类似Thread
,直接调用FutureTask
的run
方法和调用Thread
的run
类似,都是调用run
方法的线程来处理任务。
同时当调用get()
方法的时候,主线程会等待FutureTask
执行完成或者是抛出异常才会执行下去。
取消操作
虽然标题为取消操作的例子,其实还是会涉及Future
的其他API
的使用:
/**
* 取消Future的例子
* @author RJH
* @date 2018年1月31日
*/
public class FutureCancelDemo {
public static void main(String[] args) {
//任务耗时
long sleepTime=1000;
//构建随机休眠的Callable对象
Callable<Long> task=new LongTimeTask(sleepTime);
//构建线程池
ExecutorService executorService=Executors.newSingleThreadExecutor();
//记录起始时间
long startTime=System.currentTimeMillis();
//分配线程执行Callable任务,并返回Future对象
Future<Long> future=executorService.submit(task);
try {
//主线程休眠任务耗时所需的时间的一半
Thread.sleep(sleepTime/2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//判断Future是否已经完成,如果未完成则取消
if(!future.isDone()){
//不触发中断来取消Future
future.cancel(false);
}
try {
//判断Future是否被取消了
if(future.isCancelled()){
System.out.println("Future has been cancelled");
}else{
//输出任务耗时
System.out.println("Future UsedTime:"+future.get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
//输出主线程运行时间
System.out.println("Main UsedTime:"+(System.currentTimeMillis()-startTime));
//关闭线程池
executorService.shutdown();
}
}
输出结果
Future has been cancelled
Main UsedTime:502
分析
这个例子展示了怎么获取Future
的状态。使用cancel()
方法的时候需要注意,如果取消成功之后再调用get()
方法会抛出java.util.concurrent.CancellationException
。
所以如果调用过cancel()
方法的话,需要在调用get()
方法之前调用isCancelled
来判断一下是否取消成功。