Quartz

一、分类

从实现的技术上来分,主要有三种:

1) Java自带的java.util.Timer类,这个类允许调度一个java.util.TimerTask任务。使用这种方式可以让程序按照某一个周期执行,但不能在指定时间运行。一般用的较少。

2) 使用Quartz,这是一个功能比较强大的的调度器,可以让程序在指定时间执行,也可以按照某一个周期执行,配置起来会稍微复杂一些。

3) Spring3.0以后,Spring官方自主开发了一款定时任务工具–Spring Task,可以将它看作一个轻量级的Quartz,而且使用起来很简单,除了Spring相关的包外不需要额外的包,而且支持注解XML配置两种形式。

二、Quartz和Spring Task对比

  1. 实现:Task注解实现方式,比较简单。Quartz需要手动配置Jobs

  2. 任务执行:Task默认单线程串行执行任务,多任务时若某个任务执行时间过长,后续任务会无法及时执行。Quartz采用多线程,无这个问题。

  3. 调度:Task采用顺序执行,若当前调度占用时间过长,下一个调度无法及时执行;Quartz采用异步,下一个调度时间到达时,会另一个线程执行调度,不会发生阻塞问题。

  4. 部署:Quartz可以采用集群方式,分布式部署到多台机器,分配执行定时任务。

三、Quartz

Quartz最核心的三个概念分别是: 任务(Job)、触发器(Trigger)和调度器(Scheduler)。

Job&JobDetail

Job定义:实现业务逻辑的任务接口,Job接口非常容易实现的,只有一个execute方法,在里面编写业务逻辑。

1
2
3
public interface Job {
public void execute(JobExecutionContext context) throws JobExcutionException;
}

自定义Job实现类:

1
2
3
4
5
6
7
8
9
public class HelloJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 打印当前的执行时间,格式为2018-08-08 08:08:08
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Current Exec Time is:" + sdf.format(System.currentTimeMillis()));
//编写具体的业务逻辑
System.out.println("Hello World!");
}
}

Job实例在Quartz中的生命周期:每次调度器执行job时,它在调用execute方法前会创建一个新的job实例。当调用完成后,job对象实例会被释放,释放的实例会被垃圾回收机制回收。

JobDetail用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。简单来说就是用来绑定Job,会携带一些Job没有携带但又需要的信息。

1
2
3
4
//创建一个JobDetail实例,将该实例与HelloJob Class绑定
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();

Trigger

Trigger是什么?用来告诉调度程序 Job什么时候触发,即Trigger对象是用来触发执行Job的。

Trigger主要分为两类:SimpleTriggerCronTrigger

SimpleTrigger的作用:在一个指定时间段内执行一次作业任务或是在指定的时间间隔内多次执行作业任务。

CronTrigger的作用:基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更常用。

1
2
3
4
5
6
//创建一个Trigger实例,定义该job立即执行,并且每隔两秒钟执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ? "))
.build();
cron表达式

cron表达式的格式如下:

[秒] [分] [时] [日期(具体哪天)] [月] [星期]

spring 4.x中已经不支持7个参数的cron表达式了,要求必须是6个参数。

字 段 是否必填 允许值和允许特殊字符
允许的值范围是0-59,支持的特殊符号包括 , - * /,表示特定的某一秒才会触发任务,-表示一段时间内会触发任务,*表示每一秒都会触发,/表示从哪一个时刻开始,每隔多长时间触发一次任务。
允许的值范围是0-59,支持的特殊符号和秒一样。
允许的值范围是0-23,支持的特殊符号和秒一样。
日期 允许的值范围是1-31,支持的特殊符号相比秒多了?,表示与{星期}互斥,即意味着若明确指定{星期}触发,则表示{日期}无意义,以免引起冲突和混乱。
允许的值范围是1-12(JAN-DEC),支持的特殊符号与秒一样。
星期 允许值范围是1~7(SUN-SAT)1代表星期天(一个星期的第一天),以此类推,7代表星期六,支持的符号相比秒多了?,表达的含义是与{日期}互斥,即意味着若明确指定{日期}触发,则表示{星期}无意义。

cron表达式举例:

1
2
3
4
5
6
7
8
9
30 * * * * ? 每分钟的第30秒触发一次任务
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 15 10 ? * * 每天10点15分触发
0 0/5 14 * * ? 每天下午的2点到2点59分(整点开始,每隔5分触发)
0 12 10 ? * MON-FRI 从周一到周五每天上午10点15分触发

Scheduler

调度器是用来定期定频率执行任务的。
所有的Scheduler实例是由SchedulerFactory来创建的。SchedulerFactory对象通过调用getScheduler方法就能创建和初始化调度器对象。

1
2
3
4
5
//创建一个Scheduler实例
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();

四、Spring&Quartz

除了Spring基本依赖之外还需要加入以下依赖:

1
2
3
4
5
6
7
8
9
10
11
1 <dependency>
2 <groupId>org.springframework</groupId>
3 <artifactId>spring-context-support</artifactId>
4 <version>4.2.6.RELEASE</version>
5 </dependency>
6 <dependency>
7 <groupId>org.quartz-scheduler</groupId>
8 <artifactId>quartz</artifactId>
9 <version>2.2.1</version>
10 </dependency>

基于特定基类

1. 编写任务类,该类需要继承自QuartzJobBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.shallowan.quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class SecondCron extends QuartzJobBean {
private static final Logger logger = LoggerFactory.getLogger(SecondCron.class);
private Integer timeout;
//调度工厂实例化后,经过timeout时间开始执行调度
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("定时任务2进行中.......");
// do something else
}
}
2.在Spring配置文件中配置作业类JobDetailFactoryBean、作业调度的触发方式(触发器)、调度工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<bean name="secondCron" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.shallowan.quartz.SecondCron"/>
</bean>
<!--按照一定频率的触发器-->
<!--<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="secondCron"/>
<property name="startDelay" value="0"/>
<property name="repeatInterval" value="2000"/>
</bean>-->
<!--Cron表达式触发器-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="secondCron"/>
<property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
<!--配置调度工厂-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!--<ref bean="simpleTrigger"/>-->
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
</beans>

不基于特定的基类

1.编写任务调度类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.shallowan.quartz;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 基于Spring整合Quartz进行完成定时任务
*/
public class ThirdCron {
private static final Logger logger = LoggerFactory.getLogger(ThirdCron.class);
public void executeJob() {
logger.info("定时任务3进行中.......");
// do something else
}
}
2.在spring配置文件中配置作业类MethodInvokingJobDetailFactoryBean、作业调度的触发方式(触发器)、调度工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean name="thirdCron" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="com.shallowan.quartz.ThirdCron"/>
</property>
<property name="targetMethod" value="executeJob"/>
<!--作业不并发调度-->
<property name="concurrent" value="false"/>
</bean>
<!--按照一定频率的触发器-->
<!--<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="thirdCron"/>
<property name="startDelay" value="0"/>
<property name="repeatInterval" value="2000"/>
</bean>-->
<!--Cron表达式触发器-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="thirdCron"/>
<property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
<!--配置调度工厂-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!--<ref bean="simpleTrigger"/>-->
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
</beans>




Quartz属于重量级的定时任务框架,一般都会选择轻量级的如Spring Task定时任务进行开发,但是遇到比较棘手的,这也是一种解决问题的方式。




分享