Java API提供FutureTask类作为一种可撤销异步计算,此类实现了Runnable和Future接口并提供Future接口的基础实现。通过使用Callable或Runnable(Runnable对象不返回结果,所以假若Future对象返回结果的话,就需要传参)对象创建FutureTask类。此类提供取消执行以及获得计算结果的方法,还提供名为done()的方法允许在执行器中的任务运行完成后再执行一些代码。此类用来进行后处理操作,例如生成报告,通过电子邮件发送结果,或者释放资源。当FutureTask对象正在控制的任务执行完成时,done()方法被FutureTask类内部调用。此方法在任务结果被设置并且其状态变为isDone之后调用,而不考虑任务是否已经被正常地取消或者结束。
默认情况下,此方法为空,通过重写FutureTask类和实现此方法改变特性。在本节中,学习如何重写此方法在任务执行之后运行代码。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤完成范例:
-
创建名为ExecutableTask的类,实现String类参数化的Callable接口:
public class ExecutableTask implements Callable<String>{
-
定义名为name的私有String属性,存储任务名称。实现getName()方法,返回属性值:
private final String name; public String getName() { return name; }
-
实现类构造函数,初始化任务名称:
public ExecutableTask(String name){ this.name = name; }
-
实现call()方法,设置任务随机休眠一段时间,并返回任务名称的信息:
@Override public String call() throws Exception { try{ long duration = (long)(Math.random() * 10); System.out.printf("%s : Waiting %d seconds for results.\n", this.name, duration); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) {} return "Hello, World. I am " + name; }
-
实现名为ResultTask的类,继承String类参数化的FutureTask类:
public class ResultTask extends FutureTask<String>{
-
定义名为name的私有String属性,存储任务名称:
private final String name;
-
实现类构造函数,需要接收Callable对象作为参数。调用父类的构造函数并使用任务接收到的属性初始化名称属性值:
public ResultTask(ExecutableTask callable) { super(callable); this.name = callable.getName(); }
-
重写done()方法,判断isCancelled()方法的值,根据返回值,输出不同的信息到控制台:
@Override protected void done() { if(isCancelled()) { System.out.printf("%s : Has been canceled\n" , name); } else { System.out.printf("%s : Has finished\n", name); } }
-
实现范例的主方法,创建一个包含main()方法的Main类:
public class Main { public static void main(String[] args) {
-
使用Executors类的newCachedThreadPool()方法创建ExecutorService:
ExecutorService executor = Executors.newCachedThreadPool();
-
创建数组存储五个ResultTask对象:
ResultTask resultTasks[] = new ResultTask[5];
-
初始化ResultTask对象,对数组中的每个元素,首先使用对象创建ExecutorTask,然后创建ResultTask。接下来使用submit()方法发送ResultTask到执行器:
for(int i = 0 ; i < 5 ; i ++){ ExecutableTask executableTask = new ExecutableTask("Task " +i); resultTasks[i] = new ResultTask(executableTask); executor.submit(resultTasks[i]); }
-
设置主线程休眠5秒:
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
-
取消发送到执行器的所有任务:
for (int i=0; i<resultTasks.length; i++) { resultTasks[i].cancel(true); }
-
使用ResultTask对象的get()方法,输出未被取消的任务结果到控制台:
for (int i = 0 ; i < resultTasks.length ; i ++){ try { if(!resultTasks[i].isCancelled()) { System.out.printf("%s\n", resultTasks[i].get()); } } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
-
使用shutdown()方法结束执行器:
executor.shutdown(); } }
工作原理
当正在被控制的任务完成其执行时,done()方法被FutureTask类调用。本范例中,实现了Callable对象、ExecutableTask类,以及FutureTask类的子类,用来控制ExecutableTask对象的执行。
done()方法在生成结果值且改变任务状态为isDone之后被FutureTask类内部调用。你无法改变任务结果值或者改变其状态,但是你能够关闭被任务使用的资源,输出日志信息,或者发送通知。FutureTask类可以用来确保特殊的任务只能运行一次,因为调用其run()方法将只执行封装的Runnable/Callable接口一次(并且可用的情况下,使用get能够得到结果)。
更多关注
本章“执行器中运行返回结果的任务“小节。