4.1 执行器的高级特性
执行器是一个类,它允许编程人员执行并发任务而无须担心线程的创建和管理。编程人员创建Runnable对象并将其发送给执行器,而执行器创建和管理必要的线程以执行这些任务。第3章曾介绍执行器框架具有以下基本特征。
- 如何创建执行器以及在创建执行器时有哪些不同选项。
- 如何将并发任务发送给执行器。
- 如何控制执行器使用的资源。
- 在执行器的内部,如何使用一个线程池优化应用程序的性能。
无论如何,执行器提供了很多选项,这使其成为并发应用程序中的一种强大机制。
4.1.1 任务的撤销
将任务发送给执行器之后,还可以撤销该任务的执行。使用submit()方法将Runnable对象发送给执行器时,它会返回Future接口的一个实现。该类允许你控制该任务的执行。该类有cancel()方法,可用于撤销任务的执行。该方法接收一个布尔值作为参数,如果接收到的参数为true,那么执行器执行该任务,否则执行该任务的线程会被中断。
以下便是想要撤销的任务无法被撤销的情形。
- 任务已经被撤销。
- 任务已经完成了执行。
- 任务正在执行而提供给
cancel()方法的参数为false。 - 在API文档中并未说明的其他原因。
cancel()方法返回了一个布尔值,用于表明当前任务是否被撤销。
4.1.2 任务执行调度
ThreadPoolExecutor类是Executor接口和ExecutorService接口的基本实现。但是Java并发API为该类提供了一个扩展类,以支持预定任务的执行,这就是ScheduledThreadPoolExeuctor类。你可以进行如下操作。
- 在某段延迟之后执行某项任务。
- 周期性地执行某项任务,包括以固定速率执行任务或者以固定延迟执行任务。
4.1.3 重载执行器方法
执行器框架是一种非常灵活的机制。你可以通过扩展一个已有的类(ThreadPoolExecutor或者ScheduledThreadPoolExecutor)实现自己的执行器,获得想要的行为。这些类中包括一些便于改变执行器工作方式的方法。如果你重载了ThreadPoolExecutor类,就可以重载以下方法。
beforeExecute():该方法在执行器中的某一并发任务执行之前被调用。它接收将要执行的Runnable对象和将要执行这些对象的Thread对象。该方法接收的Runnable对象是FutureTask类的一个实例,而不是使用submit()方法发送给执行器的Runnable对象。afterExecute():该方法在执行器中的某一并发任务执行之后被调用。它接收的是已执行的Runnable对象和一个Throwable对象,该Throwable对象存储了任务中可能抛出的异常。与beforeExecute()方法相同,Runnable对象是FutureTask类的一个实例。newTaskFor():该方法创建的任务将执行使用submit()方法发送的Runnable对象。该方法必须返回RunnableFuture接口的一个实现。默认情况下,Open JDK 9和Oracle JDK 9返回FutureTask类的一个实例,但是这在今后的实现中可能会发生变化。
如果扩展ScheduledThreadPoolExecutor类,你可以重载decorateTask()方法。该方法与面向预定任务的newTaskFor()方法类似并且允许重载执行器所执行的任务。
4.1.4 更改一些初始化参数
你也可以在执行器创建之时更改一些参数以改变其行为。最常用的一些参数如下所示。
BlockingQueue:每个执行器均使用一个内部的BlockingQueue存储等待执行的任务。可以将该接口的任何实现作为参数传递。例如,更改执行器执行任务的默认顺序。ThreadFactory:可以指定ThreadFactory接口的一个实现,而且执行器将使用该工厂创建执行该任务的线程。例如,你可以使用ThreadFactory接口创建Thread类的一个扩展类,保存有关任务执行时间的日志信息。RejectedExecutionHandler:调用shutdown()方法或者shutdownNow()方法之后,所有发送给执行器的任务都将被拒绝。可以指定RejectedExecutionHandler接口的一个实现管理这种情形。
