Quartz调度框架

Quartz是什么

Quartz是一个开源的作业调度包,能够运行在几乎任何java项目中,小到单机应用,大到电商系统。Quartz能够创建很容易的调度,也可以创建十个、百个、千个、甚至万个任务的复杂调度。Quartz将任务定义成java组件,能够执行几乎任何你定义的事情。Quartz也支持许多企业级特性,比如JTA和集群。
Quartz的核心是Scheduler、Job、Trigger。Job负责定义需要执行的任务,Trigger负责调度策略,Scheduler负责将二者组合。

Quartz官网:http://www.quartz-scheduler.org/
quartz核心包quartz-2.2.1.jar
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
</pre>
如果想使用quartz内置的job还需要导入quartz-jobs
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
</pre>

简单Demo

调度的任务,需要实现Job接口,实现execute()方法。execute方法就是需要执行的任务。
<pre>
public class MyJob implements Job {

public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    System.out.println("Hello World,Hello Quartz!");
}

}
</pre>

调度流程
1、通过工厂方法创建调度器(quartz scheduler生命周期的开始)
2、启动调度器
3、定义作业
4、创建触发器
5、组合任务和触发器
6、调度结束(quartz scheduler生命周期结束)
<pre>
public class HelloQuartz {
public static void main(String[] args){
try {
//通过工厂方法创建调度器,整个程序不会停止,直到调用scheduler.shutdown()
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//调度启动
scheduler.start();
//定义作业,并且绑定我们指定的作业类
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").build();
//创建触发器
Trigger trigger = newTrigger().withIdentity("myTrigger","group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(20).repeatForever()).build();
//调度器添加触发器和作业,开始调度
scheduler.scheduleJob(job,trigger);
//调度关闭
// scheduler.shutdown();
}catch (SchedulerException e){
e.printStackTrace();
}
}
}
</pre>
Quartz的API设计使用的是 DSL(domain specific language),所以非常干净。
如果你直接运行上面的代码应该会抛出:
<pre>
org.quartz.SchedulerConfigException: Thread count must be > 0
at org.quartz.simpl.SimpleThreadPool.initialize(SimpleThreadPool.java:242)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1288)
at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
at org.quartz.impl.StdSchedulerFactory.getDefaultScheduler(StdSchedulerFactory.java:1535)
at com.yjz.quartz.HelloQuartz.main(HelloQuartz.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
</pre>
提示线程池中初始化的线程要大于0,那么这个线程池数量在哪里配置?原来Quartz使用一个quartz.properties的配置文件,你可以在你项目中classpath创建该文件(web项目在resources下)。quartz的配置文件还是比较好配置的,可以现简单的配置几项:
<pre>
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
</pre>

quartz核心接口

Scheduler:与调度器交互的主要API
Job:任务组件实现的接口
JobDetail:用来定义任务的实例
JobBuilder:用于定义JobDetail实例,定义作业实例
Trigger:为任务定义执行计划的组件
TriggerBuilder:用来定义Trigger实例
Scheduler只有调用了start()方法之后才能触发任务执行,quartz的整个生命周期从通过SchedulerFactory创建Scheduler实例到调用shutdown()方法。

Jobs

之前说过每个job要实现Job接口,job接口里面中定义了指定:
<pre>
public interface Job {
void execute(JobExecutionContext context)
throws JobExecutionException;
}
</pre>
当任务触发后,调度器线程会调用excute()方法,JobExecutionContext提供了job运行时的信息,Scheduler、Trigger、JobDetail等都执行它。每次调度器执行job,都会新建job实例,每次job执行完成后,这个实例会被垃圾回收。还有就是这个job必须有一个无参的构造函数(调度器内部要使用)。
JobDetail:客户端(我们程序)创建JobDetail,并且添加到调度器中,JobDetail包含了job的设置信息。
JobDataMap是JobDetail的一部分,它可以包含任何数量的数据对象,可以用来显示执行过程。JobDataMap实现java Map接口,提供了一些原始的存储和获取。
向JobDataMap中添加数据:
<pre>
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").usingJobData("key1","value1").usingJobData("key2",2).build();
</pre>
获取JobMapData:
<pre>
public class MyJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String k1 = dataMap.getString("key1");
int k2 = dataMap.getInt("key2");
System.out.println("Hello World,Hello Quartz!");
}
}
</pre>
JobMapData可以在job中添加一些值,比如:
<pre>
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
ArrayList state = (ArrayList)dataMap.get("myStateData");
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
</pre>
通过context.getMergedJobDataMap()获取,然后回去对应的state值,然后向state中添加值。或者通过注射,在job类中定义对应的setXxx()实现:
<pre>
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 dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
public void setJobSays(String jobSays) {
this.jobSays = jobSays;
}
public void setMyFloatValue(float myFloatValue) {
myFloatValue = myFloatValue;
}
public void setState(ArrayList state) {
state = state;
}
}
</pre>

Trigger

trigger对象用来触发任务执行。当需要调度一个任务时候,需要实例化一个trigger,并且调整参数来给调度器使用。所有的Trigger类型都有TriggerKey属性,用来追踪确定它们的身份。还有一些其它通用参数通过TriggerBuilder构建trigger过程中指定的。
jobKey:用来表明当trigger触发执行时要执行的任务。
startTime:用来表明触发器什么时候开始触发执行。这个值是java.util.Date对象定义的时间。
endTime:用来表明调度触发器什么时候失效。
trigger有多种触发类型,比较常用的有SimpleTrigger和CronTrigger:

SimpleTrigger

当一个任务在一个特定时刻执行一次,或在特定时刻时刻重复执行,可以使用SimpleTrigger。例如你想2017年1月10日 12:32:24执行,或者每十秒执行五次。SimpleTrigger中的开始时间(startTime)、结束时间(endTime)、重复次数(repeat)、重复间隔(repeat interval)。重复可以为0或者一个整数,还可以为SimpleTrigger.REPEAT_INDEFINITELY;重复间隔必须是0或者一个long型,代表毫秒数,如果设置为0将会造成重复触发器同时执行。

CronTrigger

如果你需要基于日历的调度,可以使用CronTirgger,CronTrigger比SimpleTrigger更常用。例如每个月的星期五、每个工作日的九点、每周一九点到十点的每五分钟。
CronTrigger使用Cron-Expressions配置,Cron-Expressions使用以下七种表达式(Sub-Expression)表示:
1、Seconds
2、Minutes
3、Hours
4、Day-of-Month
5、Month
6、Day-of-Week
7、Year
例如:”0 0 12 ? * WED”代表每周三的12:00:00pm
每个二级表达式可以是一个列表,例如:”MON,WED,FRI”
表达式介绍:

  • 通配符“xxx”代表的是“每次xxxx时候”,“MON-WED,SAT”每周一到周三和周六。
  • ”*”:代表0次或者多次,在Day-Of-Week中代表着”一周中的每一天”。每个字符都需要有效,比如分钟需要在059,小时需要是023,Day-Of-Month必须是131(注意月份的天数),月份是011或者使用:JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC,Day-Of-Week是1~7(1表示周日)或者是SUN, MON, TUE, WED, THU, FRI and SAT。
  • ”/“:代表值的增量,例如0/15在分钟里(还可以用0,15,30,45) ,代表的是从0,每小时的15分钟。/35在分钟表达式中代表是每小时的第35分钟,从0开始(还可以表示为0,35)。
  • “?”:用在day-of-month和day-of-week里面,用来表示不确定值。
  • “L”:用在day-of-month和day-of-week里面,在day-of-month代表每个月的最后一天比如1月的31号和4四月的30号。在day-of-week中代表每周最后一天7(周六)。也可以用在day-of-week后一个字段值,意识是每个月的最后第几天,例如6L,每个月的最后一个星期五,也可以指定偏移量L-3代表每个月的最后第三天。L不能使用指定的列表中,否则会引起混乱。
  • “W”:代表的意思是工作日,例如15W,这个月的第15个工作日。
  • “#”:代表每个月xxx工作日,例如在day-of-week中6#3,每个月的第三个周五。

例子:
“0 0/5 * * *?” 每五分钟
“10 0/5 * * *?”每五分钟10秒之后
“0 30 10-13 ? * WED,FRI”代表周三周五的10:30,11:30,12:30,13:30
“0 0/30 8-9 5,20 * ?”代表的是每个月5号和20的8点到9点的每半个小时。
代码中使用:
<pre>
Trigger trigger1 = newTrigger().withIdentity("myTrigger","group1").withSchedule(cronSchedule("0 0/2 8-17 * * ?")).forJob("myJob","group1").build();
</pre>
Quartz将任务和触发分离,这样有很多好处,比如:任务创建和存储与触发器相互独立;许多触发器可以触发同一个任务;还可以任务不变的情况下修改和重新定义触发器,这样有效的对调度器进行了解耦合。
Jobs和Trigger还定义了key,注册到调度器中,这样允许将这些key放到不同的分组中,这个功能很实用于一个公司内多个部门,每个部门使用自己的组,这个key是名称和分组的组合,并且这个key唯一。
ScheduleBuilder有多种类型

Quartz的一些主要特性就是这些,当然还有配置和API可以以后再说。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容