三种基本的Java任务调度工具比较

1、Timer工具类

Timer是JDK自带的任务调度工具类,它只需要java.util.Timer和java.util.TimerTask两个类就可以实现基本任务调度功能。

其中TimerTask实现了Runnable接口的抽象类,开发人员只需要继承TimerTask并实现run方法,将任务内容写入run方法中,然后就可以将其交给Timer去调度执行了。

Timer类的调度方法有如下几种:

三种基本的Java任务调度工具比较_第1张图片

其中,task是需要被执行的任务,其它参数是调度计划的设置。

Timer类的核心是它的两个内部类TaskThread和TaskQueue。

在Timer创建的同时会初始化TaskThread和TaskQueue对象,并调用TaskThread的start方法启动该线程。Timer通过上述的schedule方法会将需要调度的TimerTask放入TaskQueue中,每次往TaskQueue放入新的TimerTask时, TaskQueue会按照任务的执行时间由小到大进行排序。

TimerThread线程在start方法启动后,就会开始不断轮询,每次轮询都会获取TaskQueue中第一个TimerTask( 执行时间最小的TimerTask),判断当前是否已到执行时间:

如当前时间大于或等于执行时间,则执行TimerTask;

如未到,则会休眠一段时间(时长=任务执行时间-当前时间)。

执行后,判定该task是否需要重复执行,如需要,则重置该task的执行时间,重新放入TaskQueue中(放入后会自动排序)。

Timer工具类

优点:JDK本身就自带该工具类,无需第三方依赖,只需实现TimerTask类即可使用Timer进行调度配置,使用起来简单方便。

缺点:Timer中所有的任务都一个TaskThread线程来调度和执行,任务的执行方式是串行的,如果前一个任务发生延迟或异常会影响到后续任务的执行。

2、ScheduledExecutorService

为了解决上述Timer缺陷,Sun公司在JDK1.5中引入了ScheduledThreadPoolExecutor类,它继承自ThreadPoolExecutor类并实现ScheduledExecutorService接口,故其能使用线程池来实现任务调度。

ScheduledExecutorService包含如下接口:

:scheduleAtFixedRate方法是基于初始延迟(initialDelay)后固定间隔(period)进行任务调度;scheduleWithFixedDelay方法是基于上次任务完成后固定的延迟时间来进行任务调度。两者的任务执行的维度不同。

ScheduledThreadPoolExecutor与Timer一样也有两个核心的内部类:DelayedWorkQueue和ScheduledFutureTask。

如上所示它的schedule方法接收一个Runnable接口的子类后,会将其包装成ScheduledFutureTask放入DelayedWorkQueue。DelayedWorkQueue是有序队列,会对新加入到队列的ScheduledFutureTask进行排序处理(按执行时间从小到大)。

而后DelayedWorkQueue中的任务会被调度线程从线程池中分配一个固定的线程进行调用,调用时执行run的方法会判断是否是周期性任务来决定是否要设置下次执行时间,以便下次执行。

ScheduledFutureTask的run方法如下:

三种基本的Java任务调度工具比较_第2张图片

ScheduledThreadPoolExecutor相对于Timer因为使用了线程池,所有任务都会单独分配一个线程去执行,故不会互相影响。在大部分情况下,推荐使用ScheduledThreadPoolExecutor来实现任务调度而非Timer。

ScheduledExecutorService虽然解决了Timer由于单线程导致的问题,但从上述schedule方法可以看出它是基于延迟(initialDelay)来设定具体执行时间的,虽然可以通过计算实现某些复杂的作业调度配置,但这种用法过于繁杂而且执行时间不够明确。

某些特殊的执行条件,如固定每个月几号执行,或是每天多个非固定间隔的时间点去执行同一任务等需求就无法实现,对于节假日等也无法控制,对此我们可以使用Quartz来实现这些相对复杂的调度需求。

3、Spring Quartz Scheduler

Spring Quartz是在Spring框架中使用Quartz工具来实现任务调度的方式,在Spring下对Quartz可以方便的完成任务调度需要的配置。

Quartz是一个相对上述两种调度工具更为复杂的任务调度系统,使用Trigger, Job 和JobDetail对象来实现对各种类型任务的调度。Spring也需要配置好这些对象,才能使用Quartz的任务调度功能。

Spring提供了一个JobDetailFactoryBean用于配置JobDetail,在JobDetail中包含了所有运行job (ExampleJob)需要的信息。让我们来看一下的例子:

三种基本的Java任务调度工具比较_第3张图片

其中ExampleJob是实现了Job接口的QuartzJobBean继承类,它是真正的任务执行者。具体实现如下所示:

三种基本的Java任务调度工具比较_第4张图片

Quartz的触发器是之前两种调度工具最大的区别,Quartz实现了两个常用的触发器SimpleTrigger和CronTrigger,SimpleTrigger可配置简单的执行计划。CronTrigger则可以根据具体的Corn表达式配置各种复杂的执行计划,满足各种特殊的需求。

Srping可以通过CronTriggerFactoryBean 和 SimpleTriggerFactoryBean来进行Trigger的配置。配置方法如下:

三种基本的Java任务调度工具比较_第5张图片

最后,只需要配置通过SchedulerFactoryBean来配置Scheduler,将Trigger注册到具体的Scheduler中,由其进行触发调度。

Spring即可以成功使用Quartz实现任务调度:

三种基本的Java任务调度工具比较_第6张图片

Quartz同ScheduledThreadPoolExecutor一样也是基于线程池进行任务调度的,它默认使用org.quartz.simpl.SimpleThreadPool来作为线程池,在调用scheduleJob()方法会将Job和Trigger存储在JobStore(从存储介质中获取触发器,存储介质可以是内存也可以是数据库)中,然后通知调度线程(QuartzSchedulerThread)从JobStore中获取即将被触发的触发器,到达触发时间后分配线程去执行触发器对应的Job任务。

本文作者:邱志辉(点融黑帮),目前就职于点融网工程部FinCore team,喜欢电影、旅行,以及一切新事物。

你可能感兴趣的:(三种基本的Java任务调度工具比较)