博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Quartz---任务调度入门学习
阅读量:2047 次
发布时间:2019-04-28

本文共 16341 字,大约阅读时间需要 54 分钟。

文章目录

一、什么是Quartz?

Quartz是一个完全由java编写的开源作业调度框架,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。

二、Quartz的体系结构

在这里插入图片描述

三、Quartz的基本入门案例

第一步、导入相应的jar包

org.quartz-scheduler
quartz
2.3.2
org.quartz-scheduler
quartz-jobs
2.3.2
org.slf4j
slf4j-log4j12
2.0.0-alpha1
log4j
log4j
1.2.17

第二步、新建一个任务类(Task/Job),实现Job接口,重写execute方法

public class helloJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String test = dateFormat.format(date); //工作内容 System.out.println("数据库备份成功,当前时间为:"+test); }}

第三步、编写调度类scheduler,编写任务实例和触发器,通过scheduler将触发器与任务绑定。

public class helloScheduler {
public static void main(String[] args) throws SchedulerException {
//调度器(Scheduler),通过工厂模式从工厂中获取调度实例 Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例(JobDetail) JobDetail jobDetail = JobBuilder//加载任务类,和具体任务绑定,该任务必须实现Job接口 .newJob(helloJob.class) .withIdentity("job1", "group1")//参数一是任务名称,唯一实例;参数二是任务组的名称,将任务分组 .build(); //触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1")//参数一是触发器名称,参数二是触发器组,将触发器分组 .startNow()//马上启动触发器 .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();//每1秒触发一次 //scheduler关联任务实例和触发器 defaultScheduler.scheduleJob(jobDetail, trigger); //启动 defaultScheduler.start(); }}

四、Job和JobDetail介绍

这里是引用

创建新的Job实例演示:
我们只需要在构造函数中输出一个字符串就好了,如下图所示:
在这里插入图片描述
结果:
在这里插入图片描述
这就说明在每次调度执行Job的时候,都会实例化一个Job类。
此外,JobDetail还有四个比较重要的属性:

  • name:
  • group
  • jobClass
  • jobDataMap
    具体的作用看下图
    在这里插入图片描述
    输出结果:
    在这里插入图片描述

五、JobExecutionContext介绍

这里是引用

Job能通过JobExecutionContext上下文对象访问到Quartz运行时候的环境以及Job本身的详细数据。

public class helloJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String test = dateFormat.format(date); //获取JobDetail的内容 JobKey jobKey = jobExecutionContext.getJobDetail().getKey(); System.out.println("工作任务名称:"+jobKey.getName()+"工作任务组:"+jobKey.getGroup()); System.out.println("任务类的名称(带路径):"+ jobExecutionContext.getJobDetail().getJobClass().getName()); System.out.println("任务类的名称:"+ jobExecutionContext.getJobDetail().getJobClass().getSimpleName()); //工作内容 System.out.println("数据库备份成功,当前时间为:"+test); }}

运行结果:

工作任务名称:job1工作任务组:group1任务类的名称(带路径):com.gdpu.quartz.job.helloJob任务类的名称:helloJob数据库备份成功,当前时间为:2020-11-30 10:45:45

六、JobDataMap介绍

在这里插入图片描述

通过JobDataMap,我们可以获取JobDetail,Trigger在scheduler中传递的参数,返回给任务类Job接收,具体的操作如下:

scheduler类:

public class helloScheduler {
public static void main(String[] args) throws SchedulerException {
//调度器(Scheduler),通过工厂模式从工厂中获取调度实例 Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例(JobDetail) JobDetail jobDetail = JobBuilder//加载任务类,和具体任务绑定,该任务必须实现Job接口 .newJob(helloJob.class) .withIdentity("job1", "group1")//参数一是任务名称,唯一实例;参数二是任务组的名称,将任务分组 .usingJobData("message","任务实例传递参数")//传递参数给Job任务类 .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1")//参数一是触发器名称,参数二是触发器组,将触发器分组 .startNow()//马上启动触发器 .usingJobData("message","触发器传递参数")//传递参数给Job任务类 .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();//每1秒触发一次 //scheduler关联任务实例和触发器 defaultScheduler.scheduleJob(jobDetail, trigger); //启动 defaultScheduler.start(); }}

Job任务类:

public class helloJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String test = dateFormat.format(date); //获取Scheduler中JobDetail,trigger传递的参数 //获取JobDetail中传递的参数 JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); String message = jobDataMap.getString("message"); System.out.println("JobDetail中的参数:"+message); //获取Trigger中传递的参数 JobDataMap jobDataMap1 = jobExecutionContext.getTrigger().getJobDataMap(); String message1 = jobDataMap1.getString("message"); System.out.println("Trigger中的参数:"+message1); //工作内容 System.out.println("数据库备份成功,当前时间为:"+test); }}

同理,想要获取Trigger的内容的操作,也是同获取JobDetail的操作是类似的,包括获取Trigger的任务名组名等,这里就不展示了。通过:TriggerKey key= jobExecutionContext.getTrigger().getKey();即可。

获取其他内容,如任务的当前执行时间以及下次执行时间:
在这里插入图片描述
最后总结一点,如果我们觉得先通过Context上下文对象获取jobData,然后再获取JobMap这些方式很麻烦,如下展示的是从JobDetail获取dataMap,Trigger中获取的形式是一样的:
在这里插入图片描述
我们也可以在Job类定义一个相关类型的成员变量,例如我在Scheduler中JobDetail通过dataMap穿的value是String类型的,我在Job类就可以定义一个String类型的成员变量去接收,重写set方法即可。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
但是这个方法如果遇到同名的key,则Trigger中的value会覆盖JobDetail中的value。在这里插入图片描述

七、有状态的Job和无状态的Job

什么是有状态的Job?

有状态的Job得意思其实就是每次调用Job的时候,只会有JobDataMap这一个实例Map,而无状态的Job在每次调用Job的时候都会新建一个新的JobDataMap,就会导致一些状态信息无法储存共享,其实这就有点像我们Bean的单例和多例模式,演示如下:

//任务实例(JobDetail)        JobDetail jobDetail = JobBuilder//加载任务类,和具体任务绑定,该任务必须实现Job接口                .newJob(helloJob.class)                .withIdentity("job1", "group1")//参数一是任务名称,唯一实例;参数二是任务组的名称,将任务分组                .usingJobData("count",0)//把数值传给Job接收                .build();
//Job任务类(Job)//演示无状态的Job        ++count;//通过set方法直接获取到JobDetail传递过来的count数值,做加法运算后输出        System.out.println("当前的count数值为:"+count);        jobDataMap.put("count",count);//将新增的数值增加后返回给JobDetail任务实例
//展示结果当前的count数值为:1数据库备份成功,当前时间为:2020-11-30 11:34:01当前的count数值为:1数据库备份成功,当前时间为:2020-11-30 11:34:02当前的count数值为:1数据库备份成功,当前时间为:2020-11-30 11:34:03

我们可以很明显的看到,结果一直都是1,因为这个时候我们的Job是无状态的,每一次DataMap都是一个新的实例。我们可以通过在Job类上加上这样一个注解即可保证DataMap一直都是同一个实例。

@PersistJobDataAfterExecution
加上这个注解之后,我们的Job就变成有状态的了。
在这里插入图片描述

八、Trigger介绍

Trigger的相关实现类:这里是引用

1、SimpleTrigger

通过simpleTrigger可以简单的设置一些设置,如开始时间,结束时间,执行次数,执行间隔等,从而完成在规定的一个时间段执行一些具体的任务,如:

Job类:

public class testSimpleTrigger implements Job {
@Override public void execute(JobExecutionContext context) throws JobExecutionException {
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String test = dateFormat.format(date); //获取trigger Trigger trigger = context.getTrigger(); //获取trigger实例,一个JobKey对应一个trigger实例 TriggerKey key = trigger.getKey(); System.out.println("任务开始时间为:"+dateFormat.format(trigger.getStartTime())); System.out.println("任务结束时间为:"+dateFormat.format(trigger.getEndTime())); System.out.println("Trigger名称为:"+key.getName()+"------------"+"Trigger组名称为:"+key.getGroup()); //工作内容 System.out.println("数据库备份成功,当前时间为:"+test); }}

scheduler类:

public class simpleTriggerScheduler {
public static void main(String[] args) throws SchedulerException {
//调度器(Scheduler),通过工厂模式从工厂中获取调度实例 Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler(); //设置任务开始时间 Date startDate = new Date(); startDate.setTime(startDate.getTime()+3000);//延迟三秒后开始任务 //设置任务结束时间 Date endDate = new Date(); endDate.setTime(endDate.getTime()+10000);//任务的结束时间延迟10秒 //任务实例(JobDetail) JobDetail jobDetail = JobBuilder .newJob(testSimpleTrigger.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow()//马上启动触发器 .startAt(startDate) .endAt(endDate) //withRepeatCount表示执行次数,值得注意的是,当我们规定了指定的执行时间区间 //如果在时间区间结束了,执行任务次数没达到规定的五次,按照执行时间区间来算而不是按照执行次数来算 //表示每隔五秒执行一次,需要执行五次,但由于时间规定了只有十秒,因此最终只执行了两次 .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(5)).build(); //scheduler关联任务实例和触发器 defaultScheduler.scheduleJob(jobDetail, trigger); //启动 defaultScheduler.start(); }}

输出如下:

任务开始时间为:2020-11-30 14:09:10任务结束时间为:2020-11-30 14:09:17Trigger名称为:trigger1------------Trigger组名称为:group1数据库备份成功,当前时间为:2020-11-30 14:09:10任务开始时间为:2020-11-30 14:09:10任务结束时间为:2020-11-30 14:09:17Trigger名称为:trigger1------------Trigger组名称为:group1数据库备份成功,当前时间为:2020-11-30 14:09:15

2、CronTrigger

如果你要通过像日历那样按日程来触发任务,可以基于CronTrigger来进行任务调度。如每个工作日的上午9点,或者每隔一三五的上午9点-12点,诸如此类。

Cron表达式:

注意两点的是:
一、周中SAT表示的是7,SUN表示的是一。而非我们传统的周一到周日对应的1-7。
二、周和日字段不会同时存在,例如我们制定了日这个字段为每个月的15号,我们不能保证每个月的15号都是周一或者周二。同理,如果我们指定了每周一或者周二,日这个字段也不需要了,我们可以通过?来表示我不需要这个字段。在这里插入图片描述
Cron特殊字符相关解释:
在这里插入图片描述
具体示例:
在这里插入图片描述
写在最后,当然我们也可以不用自己手写,我们可以直接通过网上的一些在线工具直接生成Cron表达式:

九、配置、资源SchedulerFactory(了解)

public class testScheduler implements Job {
@Override public void execute(JobExecutionContext context) throws JobExecutionException {
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String test = dateFormat.format(date); //工作内容 System.out.println("数据库备份成功,当前时间为:"+test); }}
public class testJobScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//通过子类的方式获取对象 StdSchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); JobDetail jobDetail = JobBuilder .newJob(testScheduler.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow()//马上启动触发器 .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?")) .build(); //scheduler关联任务实例和触发器 Date date = scheduler.scheduleJob(jobDetail, trigger); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("scheduler开始的时间是"+dateFormat.format(date)); //启动 scheduler.start(); //Scheduler执行两秒后挂起 Thread.sleep(2000L); //挂起,暂停任务 scheduler.standby(); //中止,杀死任务后不允许重启,有一个布尔类型的参数,true会等待所有任务执行完毕,而false则会直接关闭scheduler scheduler.shutdown(); //scheduler执行5秒后自动开启 Thread.sleep(5000L); scheduler.start(); }}

十、Quartz.Properties

默认的Quartz.Properties,如果我们不想用这个文件,我们也可以在资源类目录下新建一个同名文件进行覆盖。

org.quartz.scheduler.instanceName: DefaultQuartzSchedulerorg.quartz.scheduler.rmi.export: falseorg.quartz.scheduler.rmi.proxy: falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction: falseorg.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount: 10org.quartz.threadPool.threadPriority: 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

十一、Quartz监听器(重要)

在任务调度中,监听你想关注的任务。主要的监听器有三种,分别为:

TriggerListener
JobListener
SchedulerListener
在这之前得先了解全局监听器和非全局监听器的区别:
全局监听器:能够接收所有Job/Listener的通知
非全局监听器:只能接收在其注册上的Job或者Trigger

1、JobListener

JobListener的四种方法:这里是引用

JobListener的使用方法:

第一步、编写监听类,实现JobListener接口,重写四种监听方法:

//实现JobListener接口public class MyJobListener implements JobListener {
@Override public String getName() {
String name = this.getClass().getName(); System.out.println("监听器的名称是:"+name); return name; } //前置方法 @Override public void jobToBeExecuted(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName(); System.out.println("Job的名称是"+name+ "Scheduler在JobDetails即将执行时调用的方法"); } @Override public void jobExecutionVetoed(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName(); System.out.println("job的名称是:"+name+"Scheduler在JobDetails即将执行时,但又被TriggerListener否决时调用的方法"); } //后置方法 @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String name = context.getJobDetail().getKey().getName(); System.out.println("job的名称是:"+name+"Scheduler在JobDetails执行之后调用的方法"); }}

第二步、注册监听器即可

public class JobListenerScheduler {
public static void main(String[] args) throws SchedulerException {
//调度器(Scheduler),通过工厂模式从工厂中获取调度实例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例(JobDetail) JobDetail jobDetail = JobBuilder .newJob(HelloJobListener.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow()//马上启动触发器 .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3)).build(); //scheduler关联任务实例和触发器 scheduler.scheduleJob(jobDetail, trigger); //注册局部的JobListener,表示指定的任务job //scheduler.getListenerManager().addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1","group1"))); //注册全局的JobListener //scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs()); //启动 scheduler.start(); }}

2、TriggerListener

TriggerListener的五种方法:

在这里插入图片描述

public class MyTriggerListener implements TriggerListener {
private String name; public MyTriggerListener(String name) {
this.name = name; } @Override public String getName() {
System.out.println("监听器的名称是:"+name); return name; } @Override public void triggerFired(Trigger trigger, JobExecutionContext context) {
String name = trigger.getKey().getName(); System.out.println(name+"触发器被触发"); } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
String name = trigger.getKey().getName(); System.out.println(name+"触发器没被触发"); return true;//不会执行job方法,也就是说我的Job类中的数据库备份语句不会被输出 } @Override public void triggerMisfired(Trigger trigger) {
String name = trigger.getKey().getName(); System.out.println(name+"错过触发"); } @Override public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
String name = trigger.getKey().getName(); System.out.println(name+"完成之后触发"); }}
public class TriggerListenerScheduler {
public static void main(String[] args) throws SchedulerException {
//调度器(Scheduler),通过工厂模式从工厂中获取调度实例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例(JobDetail) JobDetail jobDetail = JobBuilder .newJob(HelloJobListener.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow()//马上启动触发器 .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3)).build(); //scheduler关联任务实例和触发器 scheduler.scheduleJob(jobDetail, trigger); //注册局部的JobListener,表示指定的任务job //scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simple"), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1","group1"))); //注册全局的TriggerListener //scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simple"), EverythingMatcher.allTriggers()); //启动 scheduler.start(); }}

3、SchedulerListener

与Scheduler有关的事件,增加删除一个Trigger/Job,scheduler发生错误,关闭scheduler等。

相关方法:
在这里插入图片描述

转载地址:http://ucqof.baihongyu.com/

你可能感兴趣的文章
Leetcode Go 《精选TOP面试题》20200628 69.x的平方根
查看>>
Leetcode C++ 剑指 Offer 09. 用两个栈实现队列
查看>>
Leetcode C++《每日一题》20200707 112. 路径总和
查看>>
Leetcode C++ 《第198场周赛-2》 1519. 子树中标签相同的节点数
查看>>
Leetcode C++ 《第199场周赛》
查看>>
Leetcode C++ 《第200场周赛》
查看>>
Leetcode C++ 《第201场周赛》
查看>>
云原生 第十章 应用存储和持久化数据卷:存储快照和拓扑调度
查看>>
云原生 第十一章 应用健康
查看>>
Leetcode C++ 《第202场周赛》
查看>>
云原生 第十二章 可观测性:监控与日志
查看>>
Leetcode C++ 《第203场周赛》
查看>>
云原生 第十三章 Kubernetes网络概念及策略控制
查看>>
《redis设计与实现》 第一部分:数据结构与对象 || 读书笔记
查看>>
《redis设计与实现》 第二部分(第9-11章):单机数据库的实现
查看>>
算法工程师 面经2019年5月
查看>>
搜索架构师 一面面经2019年6月
查看>>
稻草人手记
查看>>
第一次kaggle比赛 回顾篇
查看>>
leetcode 50. Pow(x, n)
查看>>