1.ScheduledExecutorService

在有些情况下,我们可能需要事先提交一个任务,这个任务并不是立即被执行的,而是要在指定的时间或者周期性地被执行,这种任务就被称为计划任务 (Scheduled Task)。典型的计划任务包括清理系统垃圾数据、系统监控、数据备份等 。
ExecutorService 接口的子类 ScheduledExecutorService 接口定义了一组方法用于执行计划任务。ScheduledExecutorService 接口的默认实现类是 java.util.concurrent.ScheduledThreadPooIExecutor 类,它是 ThreadPoolExecutor 的一个子类。Executors提供了两个静态工厂方法用于创建ScheduledExecutorService 实例:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory threadFactory)

ScheduledExecutorService 接口定义的方法按其功能可分为以下两种。

  • 延迟执行提交的任务 。 这包括以下两个方法:
<V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit)
ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit)

上述两个方法使得我们可以采用 Callable 实例或者 Runnable 实例来表示任务。delay参数和 unit 参数一起用来表示被提交的任务自其提交的那一刻到其开始执行之间的时间差,即延时 。 上述方法的返回值类型 ScheduledFuture 继承自Future 接口,因此我们也可以利用上述方法的返回值来获取所提交的计划任务的处理结果 。

  • 周期性地执行提交的任务 。 这包括以下两个方法:
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)

它能够以固定的频率不断地执行 command 参数所指定的任务。initiaIDelay 参数和 unit 参数一起指定了一个时间偏移,任务首次执行的开始时间就是任务提交时间加上这个偏移。实际上,提交给 scheduleAtFixedRate 方法执行的计划任务,其执行周期并不一定是固定的,它会同时受 Execution Time 和 period 的影响 Interval =max(Execution Tirne,period),如果任务的每次执行总是能够在 period 指定的时间跨度内完成时,那么该任务的执行周期就是 period 指定的时间跨度,此时任务的执行周期是恒定的;如果该任务的某些次执行,其执行耗时超过了 period 指定的时间跨度,那么该任务的执行周期就会变得不固定一有时其执行周期等于 period, 有时却大于period 。

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)

scheduleWithFixedDelay 方法则能够以一定的时间间隔不断地执行 command 所指定的任务 。其中, initia!Delay 参数和 unit 参数一起指定了一个时间偏移,任务首次执行的开始时间就是任务提交时间加上这个偏移。提交给 scheduleWithFixedDelay 方法执行的计划任务的执行周期 Interval = Execution Time + delay, 其中 delay 是一个固定值,因此任务的执行周期实际上也不是固定的而是随 Execution Time 的变化而变化。

2.任务执行结果处理、异常处理与任务取消

延迟执行的任务最多只会被执行一次,因此我们利用 schedule 方法的返回值(ScheduledFuture 实例)便能获取这种计划任务的执行结果、执行过程中抛出的异常以及取消任务的执行。
周期性执行的任务会不断地被执行,直到任务被取消或者相应的ScheduledExecutorService 实例被关闭 。因此, scheduleAtFixedRate 方法、scheduleWithFixedDelay 方法的返回值 (ScheduledFuture<?>) 能够取消相应的任务,但是它无法获取计划任务的一次或者多次的执行结果。如果我们需要对周期性执行的计划任务的执行结果进行处理,那么可以考虑使用异步任务类 AsyncTask 来表示计划任务 。

提交给 ScheduledExecutorService 执行的计划任务在其执行过程中如果抛出未捕获的异常 (Uncaught Exception) , 那么该任务后续就不会再被执行。即使我们在创建ScheduledExecutorService 实例的时候指定一个线程工厂,并使线程工厂为其创建的线程关联一个 UncaughtExceptionHandler , 当计划任务抛出未捕获异常的时候该UncaughtExceptionHandler 也不会被 ScheduledExecutorService 实例调用。因此,我们必须确保周期性执行的任务在其执行过程中不会抛出任何未捕获异常。