运行多个任务处理所有结果

Executor 框架允许您执行并发任务,不需要操心线程的创建和执行。 它提供了 Future 类可用于控制在一个执行者中执行的任一任务的状态及获得其结果。

当您要等待一个任务的结束,您可使用以下2个方法:

  • 如果任务已结束执行,Future 接口的 isDone() 方法返回 true
  • ThreadPoolExecutor 类的 awaitTermination() 方法将线程催眠直到所有任务完成、调用了ThreadPoolExecutor的shutdown()方法之后。

这2个方法有一些缺点:

  • 用第一个,您只能控制任务的结束
  • 用第二个,您必须关闭执行者以等待一个线程,否则此方法调用立即返回。

ThreadPoolExecutor 类提供了一个方法允许您发送给执行者一个任务列表并等待此列表中的所有任务完成。

任务

执行3个任务,当它们结束时打印出它们的结果。

实现

本节的示例代码在 com.elanzone.books.noteeg.chpt4.sect06 package中

  • Result : 计算结果 POJO 类

    • 属性 name : String
    • 属性 value : int
  • Callable<Result> 类: Task

    • 属性 name : String
    • 构造函数初始化属性为参数值
        private String name;
    
        public Task(String name) {
            this.name = name;
        }
    
    • call() 方法 : 睡上几秒后,生成 5 个随机数求和后创建 Result 对象返回
        @Override
        public Result call() throws Exception {
            System.out.printf("%s: Staring\n", this.name);
    
            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) {
                e.printStackTrace();
            }
    
            int value = 0;
            for (int i = 0; i < 5; i++) {
                value += (int) (Math.random() * 100);
            }
    
            Result result = new Result();
            result.setName(this.name);
            result.setValue(value);
    
            System.out.println(this.name + ": Ends");
            return result;
        }
    
  • 控制类 : Main

    • 创建一个 ThreadPoolExecutor 对象
        ExecutorService executor = (ExecutorService) Executors.newCachedThreadPool();
    
    • 创建一个 Task 对象列表,内含 3 个 Task 对象
        List<Task> taskList = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Task task = new Task("Task" + i);
            taskList.add(task);
        }
    
    • 调用 ThreadPoolExecutor 类的 invokeAll() 方法获得所有任务的结果。 此方法将返回一个 List<Future<Result>> 列表
        List<Future<Result>> resultList = null;
        try {
            resultList = executor.invokeAll(taskList);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    
    • 用 shutdown() 方法停止执行者
        executor.shutdown();
    
    • 逐个输出结果信息
        System.out.println("Main: Printing the results");
        if (resultList != null) {
            for (Future<Result> future : resultList) {
                try {
                    Result result = future.get();
                    System.out.println(result.getName() + ": " + result.
                            getValue());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    

工作原理

本例演示了如何用 invokeAll() 方法将一个任务列表发送到一个执行者并等待所有任务完成。 此方法接受一个 Callable 对象列表并返回一个 Future 对象列表。 在结果列表中,一个任务对应一个 Future 对象。第一个 Future 对象对应的是 Callable 对象列表里的第一个任务,依此类推。

  1. 在结果列表的声明中 Future 接口的参数使用的数据类型必须和 Callable 对象的参数兼容。本例中2者一样都是 Result 类。
  2. 调用 invokeAll() 方法的另一个重点是将只在获得任务结果时使用 Future 对象。 当此方法结束时,所有任务已经结束。如果您调用返回的 Future 对象的 isDone() 方法, 将返回 true。

了解更多

ExecutorService 类提供了另一个版本的 invokeAll() 方法:

  • invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit): 此方法执行所有任务,并在所有任务都在给定时间前完成时返回它们的执行结果。