请像对待每一行代码一样对待每篇文章 —- 鹰嘴豆
学习目标
使用quartz开发一个定时任务管理系统
开始学习
- 学习资料
链接:quartz官网
- quartz一个简单的例子及术语
quartz有几个术语scheduler(调度器)、job(任务)、trigger(触发器)。使用执行定时任务前需要初始化一个调度器,然后定义job和触发器并加入到调度器中,具体的实现代码如下 ```java // 定义调度器并开始调度器,一旦调度器关闭(sched.shutdown)就不会触发trigger和执行job, // 直到调度器重新开始 SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); Scheduler sched = schedFact.getScheduler(); sched.start();
// 定义一个任务并将它绑定HelloJob类 JobDetail job = newJob(HelloJob.class) .withIdentity(“myJob”, “group1”) .build();
// 触发器马上执行然后每隔40分钟执行一次 Trigger trigger = newTrigger() .withIdentity(“myTrigger”, “group1”) .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build();
// 告诉quartz使用我们的触发器去调度任务 sched.scheduleJob(job, trigger);
quartz提供了一些静态方法去创建,只要导入静态方法所在的类就能直接调用静态方法
```java
import static org.quartz.JobBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.DateBuilder.*;
Scheduler - the main API for interacting with the scheduler.
Job - an interface to be implemented by components that you wish to have executed
by the scheduler.
JobDetail - 用来定义任务的实例
Trigger - a component that defines the schedule upon which a given Job
will be executed.
JobBuilder - 用来定义/构建JobDetail实例,它定义了Job的实例
TriggerBuilder - 用来定义构建触发器实例,
Scheduler
调度器的生命周期在start和shutdown方法之间,创建了调度器接口我们就可以做增加、移除、罗列任务和触发器,执行其他调度相关的操作,比如暂停一个触发器。只有调度器执行了start之后才能对触发器起作用(任务调用),Jobs
一个任务实现了Job接口它只有一个简单的方法,当任务被触发就在调度器的一个工作线程中去执行execute方法,JobExecutionContext为任务提供一个运行时的环境,从这个对象中任务可以获取到执行这个任务的调度器、触发执行这个任务的触发器、这个任务的具体的JobDetail对象和其它一些内容package org.quartz; public interface Job { public void execute(JobExecutionContext context) throws JobExecutionException; }
JobDetail
当你实现的job实现了需要定时的代码逻辑并希望你的任务拥有一些属性,这时候就需要用JobDetail。在JobDetail对象上加入Job的class类并放到调度器上,每次调度器调用这个Job的时候都会重新创建这个Job类的实例,直到任务执行完了把实例释放掉等待垃圾回收器回收,因此我们不能给这个类设置有参数的构造函数也不能设定一个有状态长时间保存的变量。JobDetail还包含了对Job的一些属性设置,像为你的任务类实例提供的一些存储的状态信息的JobDataMapJobDataMap
I、jobDataMap可以用来存放很多的数据对象,在任务执行期间这些数据都是有效的,JobDataMap是实现了Map接口,并增加了一些便利的接口用来获取和存放基本类型的数据 ```java / 定义一个任务并绑定DumbJob类 / JobDetail job = newJob(DumbJob.class) .withIdentity(“myJob”, “group1”) // name “myJob”, group “group1” .usingJobData(“jobSays”, “Hello World!”) .usingJobData(“myFloatValue”, 3.141f) .build();
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException
{
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
// 一直接通过key获取value,也可以通过下面的例子注入存放JobDataMap的属性
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
System.err.println("Instance " + key + " of DumbJob says: " + jobSays
+ ", and val is: " + myFloatValue);
}
}
II、上面通过JobDataMap提供的方法根据key来获取内容,也可以通过setter注入的方式获取,这样用IDE直接快速生成setter方法,让execute里面的代码更加的干净,注意这里的属性名就是设置在JobDataMap中key的名字,Quartz会自动调用这些setter方法设置值
```java
public class DumbJob implements Job {
String jobSays;
float myFloatValue;
ArrayList state;
public DumbJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException
{
JobKey key = context.getJobDetail().getKey();
// 触发器也有jobDataMap这里将JobDetail的JobDataMap合并在一起
// 相同的key会覆盖这点需要注意
JobDataMap dataMap = context.getMergedJobDataMap();
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: "
+ jobSays + ", and val is: " + myFloatValue);
}
// quartz会自动调用这些setter方法然后将JobDataMap里面的内容设置进来
public void setJobSays(String jobSays) {
this.jobSays = jobSays;
}
public void setMyFloatValue(float myFloatValue) {
myFloatValue = myFloatValue;
}
public void setState(ArrayList state) {
state = state;
}
}
III、Triggers也可以关联JobDataMaps,这在多个触发器使用同一个任务的时候很有用,它们独立的触发任务的时候可以用这个JobDataMap提供不同的数据输入
IV、JobExecutionContext提供了获取JobDataMaps的方法
JobDataMap dataMap = context.getMergedJobDataMap()
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
- trigger是用来触发执行job的,触发器也有JobDataMap,它将参数传递给触发的任务时很有用,quartz提供了一些不同类型的触发器,如执行简单的触发条件的SimpleTrigger ,可以设置如日历一样时间的触发器CronTrigger
- 关于job的一些运行机制
我们根据quartz提供的Job接口创建一个job类并实现execute中代码逻辑,我们可以用多个JobDetail去绑定这个Job类去实现不同的配置属性和JobDataMap传递的内容,然后把他们都增加到scheduler中。当触发器触发的时候这个JobDetail被加载,并且实例化绑定在JobDetail中的Job类,用quartz的话讲JobDetail是Job的定义,执行的Job是Job实例
调度器注册Job和Trigger时都为Job和Trigger提供了标识符作为键,允许他们放在组里面,这样分类管理任务和触发器在维护任务上是非常有用的,在组里任务和触发器的键是唯一的
关于quartz的线程池
quartz线程池默认使用SimpleThreadPool这个非常简单但是很实用,设定5个线程池可以满足不多于100个Job且Job执行之间短且一般不在同一个时间内调度、也可能设置10-100线程去执行上万个触发器平均同一个时间执行10到100个任务,我们应该根据实际需要找到合适的线程池大小。Note that if a trigger’s time to fire arrives, and there isn’t an available thread, Quartz will block (pause) until a thread comes available, then the Job will execute - some number of milliseconds later than it should have. This may even cause the thread to misfire - if there is no available thread for the duration of the scheduler’s configured “misfire threshold”.(需要注意如果触发器触发的事件达到了,但是没有有效的线程,Quartz将会阻塞暂停触发器执行,知道有线程空出来,然后Job将会执行—这将有一定数量的毫秒延迟时间,甚至可能会造成任务错过激活时间而没有执行-如果没有有效的线程为调度器配置“激活失败临界值”)链接:misfire
- quartz提供了哪些有用的api让我们能编码实现想要的功能
- 如何动态增加定时任务
- 如何暂停定时任务
学习总结
贡献者列表
编辑人 | 编辑时间 | 编辑内容 |
---|---|---|
鹰嘴豆 | 2018/12/11 | 初稿 |