获取任务的结果
Executor 框架使用 Runnable 作为其基本的任务表现形式。如果提交的任务是一次延迟计算且我们需要获取计算的结果,只通过 Runnable 是无法实现的。原因在于其 run 方法不能返回一个值或抛出一个受查异常。可以使用 Callable 或 Future 来实现。
如果向 Executor 提交一组计算任务,且希望在计算完成后获取结果,有两种方式:
- 保留与每个任务关联的 Future,然后反复使用 get 方法,同时将 timeout 参数指定为 0,从而实现轮询判断任务是否完成。
- 使用 CompletionService(完成服务)。
Callable
对于存在延迟的计算(数据库查询、网络资源下载),Callable 是一种更好的抽象,其主入口点 call 方法将返回一个值,并可以抛出一个异常,Executor 中包含一些方法可以将其它类型的任务封装成 Callable。
Future
Future 可以表示一个任务的生命周期,其提供了相应的生命周期方法来获取任务的状态、计算结果或者取消任务。Future 规范中任务的生命周期只能前进无法后退,当某个任务完成后将永远停留在 “完成” 状态上。
Future 的 get 方法可以获取计算结果:
- 如果任务已完成,get 将立即返回结果;
- 如果还未完成,get 将阻塞直到任务完成;
- 如果任务抛出了异常,get 将异常封装成 ExecutionException 并重新抛出,可以通过 getCause 来获得被封装的初始异常;
- 如果任务取消,get 将抛出 CancellationException。
有多种方法可以创建一个 Future 来描述任务:
- ExecutorService 中的所有 submit 方法都将返回一个 Future,从而将一个 Runnable 或 Callable 提交给 Executor 可以返回一个 Future 来获得任务的执行结果或取消任务。
- 可以显示地为某个指定的 Runnable 或 Callable 实例化一个 FutureTask。
ExecutorService 的实现可以改写 AbstractExecutorService 中的 newTaskFor 方法,从而根据已提交的 Runnable 或 Callable 来控制 Future 的实例化过程。
将 Runnable 或 Callable 提交到 Executor 的过程中,包含一个安全发布过程:将 Runnable 或 Callable 从提交线程发布到最终执行任务的线程。 类似的,设置 Future 结果的过程中也包含一个安全发布过程:将这个结果从计算它的线程发布到通过 get 方法获得它的线程。
CompletionService
CompletionService 将 Executor 和 BlockingQueue 融合在一起,可以将 Callable 任务提交给它,然后使用类似队列操作的 take 和 poll 方法获取已完成的结果,这些结果会在完成时被封装为 Future。
ExecutorCompletionService 实现了 CompletionService,并将计算部分委托给一个 Executor。
ExecutorCompletionService 的实现:
- 构造函数中创建一个 BlockingQueue 来保存计算完成的结果。
- 计算完成时,调用 FutureTask 的 done 方法。
- 提交某个任务时,将该任务包装为一个 QueueingFuture(FutureTask 的子类),改写子类的 done 方法,并将结果放入到 BlockingQueue 中。
- take 和 poll 方法委托给了 BlockingQueue,均为阻塞方法。