执行器中运行返回结果的任务

Java 9并发编程指南 目录

执行器中运行返回结果的任务

Executor框架的优点是支持运行返回值的并发任务,Java并发API使用如下两个接口实现此功能:

  • Callable:此接口具有call()方法,在此方法中,需要实现任务逻辑。Callable接口是参数化接口,意思是需要指明call方法将要返回的数据类型。
  • Future:此接口具有一些方法,用来得到通过Callable对象生成的结果,且管理其状态。

本节中,学习如何实现返回结果并且在执行器上运行的任务。

准备工作

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

实现过程

通过如下步骤完成范例:

  1. 创建名为FactorialCalculator的类,指定其实现具有Integer类型参数化的Callable接口:

    public class FactorialCalculator implements Callable<Integer> {
    
  2. 定义名为number的私有Integer属性,用来存储此任务用来计算的数字:

    	private final Integer number;
    
  3. 实现类构造函数,初始化类属性:

    	public FactorialCalculator(Integer number){
    		this.number=number;
    	}
    
  4. 实现call()方法,此方法返回FactorialCalculator的数字属性阶乘:

    	@Override
    	public Integer call() throws Exception {
    
  5. 首先,创建和初始化方法中用到的内部参数:

    		int result = 1 ;
    
  6. 如果数字是0或者1,返回1,否则,计算数字阶乘。为了教学目的,在两个乘法之间,设置此任务休眠20毫秒:

    		if((number == 0) || (number == 1)) {
    			result = 1;
    		} else {
    			for( int i = 2 ; i  <= number ; i ++) {
    				result *= 1;
    				TimeUnit.MILLISECONDS.sleep(20);
    			}
    		}
    
  7. 输出结果信息到控制台:

    		System.out.printf("%s : %d\n", Thread.currentThread().getName(), result);
    
  8. 返回操作结果:

    		return result;
    
  9. 实现范例的主方法,创建一个包含main()方法的Main类:

    public class Main {
    	public static void main(String[] args) {
    
  10. 使用Executors类的newFixedThreadPool()方法创建ThreadPoolExecutor来运行任务。将2作为参数传递,也就是执行器中的线程数:

    		ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
    
  11. 创建Future对象列表:

    		List<Future<Integer>> resultList = new ArrayList<>();
    
  12. 使用Random类创建随机数字生成器:

    		Random random = new Random();
    
  13. 创建重复10次的循环,每次循环中,生成一个随机数:

    		for( int i = 0 ; i <10 ; i ++) {
    			Integer number = random.nextInt(10);
    
  14. 然后,创建FactorialCalculator对象,将生成的随机数作为参数传递:

    			FactorialCalculator calculator = new FactorialCalculator(number);
    
  15. 调用执行器的submit()方法传递FactorialCalculator任务到执行器,此方法返回Future对象来管理任务,并最终得到结果:

    			Future<Integer> result = executor.submit(calculator);
    
  16. 在之前创造的列表中加入Future对象:

    			resultList.add(result);
    		}
    
  17. 创建do循环用来监控执行器的状态:

    		do {
    
  18. 首先,使用执行器的getCompletedTaskCount()方法,输出指明完成的任务数量信息到控制台:

    			System.out.printf("Main : Number of Completed Tasks : %d\n", executor.getCompletedTaskCount());
    
  19. 然后,循环列表中的10个Future对象,使用isDone()方法,输出指明其管理的任务是否完成的信息:

    			for ( int i =0 ; i <resultList.size() ; i ++ ) {
    				Future<Integer> result = resultList.get(i);
    				System.out.printf("Main : Task %d :%s\n", i ,result.isDone());
    			}
    
  20. 设置线程休眠50毫秒:

    			try {
    				TimeUnit.MILLISECONDS.sleep(50);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    
  21. 当执行器中完成的任务数量小于10时,重复此循环:

    		} while (executor.getCompletedTaskCount() < resultList.size());
    
  22. 在控制台中,输出每次任务得到的结果。循环每个Future对象,通过使用get()方法得到任务返回的Integer对象:

    		System.out.printf("Main : Results\n");
    		for( int i = 0 ; i < resultList.size(); i ++) {
    			Future<Integer> result = resultList.get(i);
    			Integer number = null;
    			try {
    				number = result.get();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (ExecutionException e) {
    				e.printStackTrace();
    			}
    
  23. 然后,打印数量到控制台:

    			System.out.printf("Main : Task %d : %d\n", i , number);
    		}
    
  24. 最后,调用执行器的shutdown()对象终止其执行:

    		executor.shutdown();
    

工作原理

在本节中,学习了如何使用Callable接口加载返回结果的并发任务。通过FactorialCalculator类实现Callable接口,包含Integer类型的结果。因此,call()方法返回一个Integer值。

范例中另一个关键点是Main类,通过使用submit()方法发送将在执行器中运行的Callable对象,这个方法将Callable对象作为参数接收,并且返回Future对象,来达到两个目的:

  • 控制任务的状态。可以取消任务,检查任务是否完成。为此,需使用isDone()方法。
  • 通过使用get()方法,获得call()方法返回的结果。此方法指导Callable对象已经完成call()方法的执行并且返回结果之后才运行。如果线程在get()方法等到返回结果时被中断,则抛出InterruptedException 异常。如果call()方法抛出异常,get()方法就会抛出ExecutionException异常。

扩展学习

当调用Future对象的get()方法,并且此对象控制的任务还没有完成时,方法则被阻塞直到任务完成。Future接口提供了get()方法的另一种形式:

  • get(long timeout, TimeUnit unit):在这个方法中,如果任务的结果无效,则等待指定的时间,如果已过指定时间且结果依然无效,此方法就会抛出TimeoutException 异常。TimeUnit是一个枚举类型的类,包含如下常量:DAYS、HOURS、MICROSECONDS、MILLISECONDS、MINUTES、NANOSECONDS、和SECONDS。

更多关注

  • 本章“创建线程执行器并控制其被拒任务”,“运行多任务并处理首个结果”,和“运行多任务并处理所有结果”小节
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值