前言
ScheduledThreadPoolExecutor可调度线程池,位于java.lang.concurrent包,在JDK1.5引入,由Doug Lea开发。它主要用来在给定的延迟之后运行任务,或者定期执行任务。
ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
ScheduledThreadPoolExecutor
该类继承自ThreadPoolExecutor,查看继承关系图,是整个Executor框架中末端实现类,也是集大成者,即包含了ThreadPoolExecutor类中的功能,又在此基础上进行了延时、或定时调度增强。
延迟任务在它们被启用后立即执行,但没有任何实时保证在启用后它们何时开始,这些任务按照先进先出 (FIFO) 提交顺序执行。
构造方法
查看源码构造方法
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
需要注意的是,ScheduledThreadPoolExecutor的构造方法不像ThreadPoolExecutor可以对7个参数都进行设置,源码中所提供的构造方法最多仅支持对corePoolSize、threadFactory、handler的设置,关于其他参数默认值的说明:
- maximumPoolSize参数被设置为Integer.MAX_VALUE
- keepAliveTime参数被设置为0,意味着线程存在空闲会立即被回收
- workQueue参数被设置为DelayedWorkQueue,DelayedWorkQueue是ScheduledThreadPoolExecutor内部实现的一个阻塞队列
当提交的任务在运行之前被取消时,一般来说,此类取消的任务不会自动从工作队列中删除,直到其延迟结束。可以通过方法setRemoveOnCancelPolicy设置为true,会使得任务在取消时立即从工作队列中删除。
尽管ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,尽量不要在运行期间通过父类引用来调整线程池的参数。例如:因为它充当使用corePoolSize线程和无界队列的固定大小的池,所以对maximumPoolSize的调整没有有用的效果。此外,将corePoolSize设置为零或使用allowCoreThreadTimeOut也是不推荐的,因为一旦它们有资格运行,这可能会使池没有线程来处理任务。
核心内部类
ScheduledThreadPoolExecutor有两个内部类DelayedWorkQueue
和ScheduledFutureTask
。
DelayedWorkQueue
DelayedWorkQueue是基于类似于DelayQueue和PriorityQueue中的基于堆的数据结构,除了每个 ScheduledFutureTask还将其索引记录到堆数组中。这消除了在取消时查找任务的需要,大大加快了删除速度,从 O(n) 降低到 O(log n),并减少了在清除之前等待元素上升到顶部而可能发生的垃圾保留。但是因为队列也可能包含不是ScheduledFutureTasks的RunnableScheduledFutures,所以不能保证有这样的索引可用,在这种情况下回退到线性搜索。所有堆操作都必须记录索引更改,主要在siftUp和siftDown中。移除后,任务的heapIndex设置为 -1。注意,ScheduledFutureTasks最多只能在队列中出现一次(对于其他类型的任务或工作队列不必如此),因此由 heapIndex 唯一标识。
查看源码
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable>
ScheduledFutureTask
执行过程
ScheduledThreadPoolExecutor类重写execute和submit方法以生成内部ScheduledFuture对象来控制每个任务的延迟和调度,为了保留功能,在子类中对这些方法的任何进一步覆盖都必须调用超类版本,这有效地禁用了额外的任务定制。
调度方法
可延时执行异步任务和可周期执行异步任务方法
/**这里传入的是实现Runnable接口的任务*/
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
/**这里传入的是实现Callable接口的任务*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
/**在initialDelay之后以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**是以上一个任务结束时开始计时,period时间过去后,立即执行*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);