5.4 小结

在本章中,你学习了与返回结果的任务打交道时用到的几种机制。这些任务都基于Callable接口,而Callable接口中声明了call()方法。该接口是一个由call()方法返回的类进行参数化的接口。

当你在执行器中执行一个Callable任务时,总是要获得Future接口的一个实现。你可以使用这个对象来撤销该任务的执行,通过该对象来知晓任务是否完成执行,或者获得call()方法所返回的结果。

你可以通过三种方式将Callable任务发送给执行器。通过submit()方法可以发送一个任务,而且将很快获得一个与该任务相关联的Future对象。通过invokeAll()方法,你可以发送一个任务列表,并且当所有任务都完成执行之后获得一个Future对象列表。通过invokeAny()方法,你可以发送一个任务列表,而且将接收到第一个执行结束且没有抛出异常的任务的结果(并不是一个Future对象)。剩余其他任务将被撤销。

Java并发API提供了另一种机制来处理这些任务类型。这种机制在CompletionService接口中定义,并且在ExecutorCompletionService类中实现。这种机制允许你将任务的执行与任务结果的处理解耦。CompletionService接口在内部使用了一个执行器,并且提供submit()方法将任务发送给CompletionService接口,还提供了poll()方法和take()方法来获取这些任务的结果。提供这些结果的顺序与任务执行完毕的顺序相同。

你还学会了如何通过两个真实的例子来实现这些理念。首先是一个针对UKACD数据集的最佳匹配算法;其次是一个倒排索引构造程序,它用到的数据集包含了从维基百科上抽取的10万多份有关电影信息的文档。

下一章,你将学会如何以一种划分为多个阶段的并发方式来执行算法。这些阶段的主要特点是,你必须在开始下一阶段之前将当前阶段的所有任务执行完毕。Java并发API提供了Phaser类,可使这些算法的并发实现更加方便。该类让你可以在一个阶段结束时同步所有参与本阶段工作的任务,因此在当前阶段执行完毕之前,任何任务都不能开始下一阶段的工作。