5.1 Callable接口和Future接口简介

执行器框架允许编程人员执行并发任务而无须创建和管理线程。你可以创建任务并将其发送给执行器,而执行器负责创建和管理所需的线程。

在执行器中,你可以执行两种任务。

  • 基于Runnable接口的任务:这些任务实现了不返回任何结果的run()方法。
  • 基于Callable接口的任务:这些任务实现了返回某个对象作为结果的call()接口。call()方法返回的具体类型由Callable接口的泛型参数指定。为了获取该任务返回的结果,执行器会为每个任务返回一个Future接口的实现。

在前面的几章中,你了解了如何创建执行器,如何基于Runnable接口向执行器发送任务,以及如何个性化定制执行器以适应你的需求。本章,你将学习如何基于Callable接口和Future接口来与任务打交道。

5.1.1 Callable接口

Callable接口是一个与Runnable接口非常相似的接口。Callable接口的主要特征如下。

  • 它是一个通用接口。它有一个简单类型参数,与call()方法的返回类型相对应。
  • 它声明了call()方法。执行器运行任务时,该方法会被执行器执行。它必须返回声明中指定类型的对象。
  • call()方法可以抛出任何一种校验异常。你可以实现自己的执行器并重载afterExecute()方法来处理这些异常。

5.1.2 Future接口

当你向执行器发送一个Callable任务时,它将为你返回一个Future接口的实现,这允许你控制任务的执行和任务状态,使你能够获取结果。该接口的主要特征如下。

  • 你可以使用cancel()方法来撤销任务的执行。该方法有一个布尔型参数,用于指定是否需要在任务运行期间中断任务。
  • 你可以校验任务是否已被撤销(采用isCancelled()方法)或者是否已经结束(采用isDone()方法)。
  • 你可以使用get()方法获取任务返回的值。该方法有两个变体。第一个变体不带有参数,当任务完成执行后,该变体将返回任务所返回的值。如果任务并没有完成执行,它将挂起执行线程直到任务执行完毕。第二个变体带有两个参数:时间周期和该周期的TimeUnit(时间单位)。该变体与第一个变体的区别在于将线程等待的时间周期作为参数来传递。如果这一周期结束后任务仍未结束执行,该方法就会抛出一个TimeoutException异常。