执行多个任务的时候,使用 Java 标准库提供的线程池是非常方便的。我们提交的任务只需要实现 Runnable 接口,就可以让线程池去执行:

  1. class Task implements Runnable {
  2. public String result; // 保存结果值
  3. public void run() {
  4. this.result = longTimeCalculation();
  5. }
  6. }

Runnable 接口有个问题,它的方法没有返回值。
如果任务需要一个返回结果,那么只能保存到变量,还要提供额外的方法读取,非常不便。所以,Java 标准库还提供了一个 Callable 接口,和 Runnable 接口比,它多了一个返回值:

  1. class Task implements Callable<String> {
  2. public String call() throws Exception { // 注意这里,于 Runnable 不同的是,这里调用的是 call
  3. return longTimeCalculation(); // 直接返回
  4. }
  5. }

并且 Callable 接口是一个泛型接口,可以返回指定类型的结果。
现在的问题是,如何获得 Callable 异步执行的结果?
如果仔细看 ExecutorService.submit() 方法,可以看到,它返回了一个 Future 类型,一个 Future 类型的实例代表一个未来能获取结果的对象:

  1. ExecutorService executor = Executors.newFixedThreadPool(4);
  2. // 定义任务:
  3. Callable<String> task = new Task();
  4. // 提交任务并获得Future:
  5. Future<String> future = executor.submit(task);
  6. // 从Future获取异步执行返回的结果:
  7. String result = future.get(); // 可能阻塞。阻塞的原因为 future 的线程还未执行完。

当提交一个 Callable 任务后,我们会同时获得一个 Future 对象,然后,我们在主线程某个时刻调用 Future 对象的 get() 方法,就可以获得异步执行的结果。在调用 get() 时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么 get() 会阻塞,直到任务完成后才返回结果。
一个 Future 接口表示一个未来可能会返回的结果,它定义的方法有:

  • get() :获取结果(可能会等待)
  • get(long timeout, TimeUnit unit) :获取结果,但只等待指定的时间;
  • cancel(boolean mayInterruptIfRunning) :取消当前任务;
  • isDone() :判断任务是否已完成。

    小结

    对线程池提交一个 Callable 任务,可以获得一个 Future 对象;
    可以用 Future 在将来某个时刻获取结果。