Java线程池ScheduledThreadPoolExecutor分析1
简介
自JDK1.5开始,JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度。在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成。但Timer有不少的缺陷:
- Timer是单线程模式;
- 如果在执行任务期间某个TimerTask耗时较久,那么就会影响其它任务的调度;
- Timer的任务调度是基于绝对时间的,对系统时间敏感;
- Timer不会捕获执行TimerTask时所抛出的异常,由于Timer是单线程,所以一旦出现异常,则线程就会终止,其他任务也得不到执行。
ScheduledThreadPoolExecutor继承ThreadPoolExecutor来重用线程池的功能,它的实现方式如下:
- 将任务封装成ScheduledFutureTask对象,ScheduledFutureTask基于相对时间,不受系统时间的改变所影响;
- ScheduledFutureTask实现了java.lang.Comparable接口和java.util.concurrent.Delayed接口,所以有两个重要的方法:compareTo和getDelay。compareTo方法用于比较任务之间的优先级关系,如果距离下次执行的时间间隔较短,则优先级高;getDelay方法用于返回距离下次任务执行时间的时间间隔;
- ScheduledThreadPoolExecutor定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间间隔的大小来排序;
- ScheduledFutureTask继承自FutureTask,可以通过返回Future对象来获取执行的结果。
类图
从ScheduledThreadPoolExecutor类声明可以看出:
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,并且实现了接口ScheduledExecutorService;
Demo
1 | package com.fly.learn.executor; |
源码解析
构造函数
1 | public ScheduledThreadPoolExecutor(int corePoolSize) { |
从构造方法可以看出,ScheduledThreadPoolExecutor使用DelayQueue来作为线程池的工作队列,由于DelayQueue是无界队列,根据线程池的工作原理,核心参数maximumPoolSize在ScheduledThreadPoolExecutor中是没有什么意义的。
总的来说,ScheduledThreadPoolExecutor为了实现周期性执行任务,对ThreadPoolExecutor做了以下改动:
- 工作队列使用DelayQueue;
- 任务提交之后统统都进工作队列;
- 获取任务的方式改变,执行了任务之后,也增加了额外的处理,具体的改变后文会一一给出详细的分析。
任务提交与调度
1 | /** |
ScheduledFutureTask
1 | // 任务被添加到ScheduledThreadPoolExecutor中的序号 |
delayedExecute
1 | private void delayedExecute(RunnableScheduledFuture<?> task) { |
总结
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,实现了ScheduledExecutorService接口,该接口定义了schedule等任务调度的方法。
同时ScheduledThreadPoolExecutor有两个重要的内部类:DelayedWorkQueue和ScheduledFutureTask。ScheduledFutureTask继承自FutureTask,并且实现了Delayed接口,DelayedWorkQueue将会在接下来的篇幅分析,敬请期待。