执行器中控制任务完成

Java 9并发编程指南 目录

执行器中控制任务完成

Java API提供FutureTask类作为一种可撤销异步计算,此类实现了Runnable和Future接口并提供Future接口的基础实现。通过使用Callable或Runnable(Runnable对象不返回结果,所以假若Future对象返回结果的话,就需要传参)对象创建FutureTask类。此类提供取消执行以及获得计算结果的方法,还提供名为done()的方法允许在执行器中的任务运行完成后再执行一些代码。此类用来进行后处理操作,例如生成报告,通过电子邮件发送结果,或者释放资源。当FutureTask对象正在控制的任务执行完成时,done()方法被FutureTask类内部调用。此方法在任务结果被设置并且其状态变为isDone之后调用,而不考虑任务是否已经被正常地取消或者结束。

默认情况下,此方法为空,通过重写FutureTask类和实现此方法改变特性。在本节中,学习如何重写此方法在任务执行之后运行代码。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤完成范例:

  1. 创建名为ExecutableTask的类,实现String类参数化的Callable接口:

    public class ExecutableTask implements Callable<String>{
    
  2. 定义名为name的私有String属性,存储任务名称。实现getName()方法,返回属性值:

    	private final String name;
    	public String getName() {
    		return name;
    	}
    
  3. 实现类构造函数,初始化任务名称:

    	public ExecutableTask(String name){
    		this.name = name;
    	}
    
  4. 实现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;
    	}
    
  5. 实现名为ResultTask的类,继承String类参数化的FutureTask类:

    public class ResultTask extends FutureTask<String>{
    
  6. 定义名为name的私有String属性,存储任务名称:

    	private final String name;
    
  7. 实现类构造函数,需要接收Callable对象作为参数。调用父类的构造函数并使用任务接收到的属性初始化名称属性值:

    	public ResultTask(ExecutableTask callable) {
    		super(callable);
    		this.name = callable.getName();
    	}
    
  8. 重写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);
    		}
    	}
    
  9. 实现范例的主方法,创建一个包含main()方法的Main类:

    public class Main {
    	public static void main(String[] args) {
    
  10. 使用Executors类的newCachedThreadPool()方法创建ExecutorService:

    		ExecutorService executor = Executors.newCachedThreadPool();
    
  11. 创建数组存储五个ResultTask对象:

    		ResultTask resultTasks[] = new ResultTask[5];
    
  12. 初始化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]);
    		}
    
  13. 设置主线程休眠5秒:

    		try {
    			TimeUnit.SECONDS.sleep(5);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
  14. 取消发送到执行器的所有任务:

    		for (int i=0; i<resultTasks.length; i++) {
    			resultTasks[i].cancel(true);
    		}
    
  15. 使用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();
    			}
    		}
    
  16. 使用shutdown()方法结束执行器:

    		executor.shutdown();
    	}
    }
    

工作原理

当正在被控制的任务完成其执行时,done()方法被FutureTask类调用。本范例中,实现了Callable对象、ExecutableTask类,以及FutureTask类的子类,用来控制ExecutableTask对象的执行。

done()方法在生成结果值且改变任务状态为isDone之后被FutureTask类内部调用。你无法改变任务结果值或者改变其状态,但是你能够关闭被任务使用的资源,输出日志信息,或者发送通知。FutureTask类可以用来确保特殊的任务只能运行一次,因为调用其run()方法将只执行封装的Runnable/Callable接口一次(并且可用的情况下,使用get能够得到结果)。

更多关注

本章“执行器中运行返回结果的任务“小节。

猜你喜欢

转载自blog.csdn.net/nicolastsuei/article/details/84549914