注:本文来源于:是Guava不是瓜娃 《定时任务框架Quartz-(一)Quartz入门与Demo搭建》java
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,彻底由Java开发,能够用来执行定时任务,相似于java.util.Timer。可是相较于Timer, Quartz增长了不少功能: 持久性做业 - 就是保持调度定时的状态; 做业管理 - 对调度做业进行有效的管理;
大部分公司都会用到定时任务这个功能。
拿火车票购票来讲,当你下单后,后台就会插入一条待支付的task(job),通常是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消这次订单;当你支付完成以后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。并发
在咱们实际的项目中,当Job过多的时候,确定不能人工去操做,这时候就须要一个任务调度框架,帮咱们自动去执行这些程序。那么该如何实现这个功能呢?框架
(1)首先咱们须要定义实现一个定时功能的接口,咱们能够称之为Task(或Job),如定时发送邮件的task(Job),重启机器的task(Job),优惠券到期发送短信提醒的task(Job),实现接口以下:
dom
(2)有了任务以后,还须要一个可以实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等。ide
(3)有了Job和Trigger后,怎么样将二者结合起来呢?即怎样指定Trigger去执行指定的Job呢?这时须要一个Schedule,来负责这个功能的实现。工具
上面三个部分就是Quartz的基本组成部分:测试
下面来利用Quartz搭建一个最基本的Demo。
ui
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; /** * 新建一个可以打印任意内容的Job: * @author admin * */ public class PrintWordsJob implements Job{ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100)); } }
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.util.concurrent.TimeUnit; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; /** * 建立Schedule,执行任务: * @author admin * */ public class MyScheduler { public static void main(String[] args) throws SchedulerException, InterruptedException { // 一、建立调度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 二、建立JobDetail实例,并与PrintWordsJob类绑定(Job执行内容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class).withIdentity("job1", "group1").build(); // 三、构建Trigger实例,每隔1s执行一次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .startNow()//当即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s执行一次 .repeatForever()).build();//一直执行 //四、执行 scheduler.scheduleJob(jobDetail, trigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); //睡眠 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println("--------scheduler shutdown ! ------------"); } }
运行程序,能够看到程序每隔1s会打印出内容,且在一分钟后结束:spa
下面就程序中出现的几个参数,看一下Quartz框架中的几个重要参数:.net
Job和JobDetail
JobExecutionContext
JobDataMap
Trigger、SimpleTrigger、CronTrigger
Job是Quartz中的一个接口,接口下只有execute方法,在这个方法中编写业务逻辑。
接口中的源码:
JobDetail用来绑定Job,为Job实例提供许多属性:
name
group
jobClass
jobDataMap
JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,而后建立该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。
为何设计成JobDetail + Job,不直接使用Job
JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。 这是由于任务是有可能并发执行,若是Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,Sheduler每次执行,都会根据JobDetail建立一个新的Job实例,这样就能够规避并发访问的问题。
JobExecutionContext中包含了Quartz运行时的环境以及Job自己的详细数据信息。
当Schedule调度执行一个Job的时候,就会将JobExecutionContext传递给该Job的execute()中,Job就能够经过JobExecutionContext对象获取信息。
主要信息有:
JobDataMap实现了JDK的Map接口,能够以Key-Value的形式存储数据。
JobDetail、Trigger均可以使用JobDataMap来设置一些参数或信息,
Job执行execute()方法的时候,JobExecutionContext能够获取到JobExecutionContext中的信息:
如:
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class).usingJobData("jobDetail1", "这个Job用来测试的") .withIdentity("job1", "group1").build(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "这是jobDetail1的trigger") .startNow()//当即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s执行一次 .repeatForever()).build();//一直执行
Job执行的时候,能够获取到这些参数信息:
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetail1")); System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("trigger1")); String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100)); }
Trigger是Quartz的触发器,会去通知Scheduler什么时候去执行对应Job。
new Trigger().startAt():表示触发器首次被触发的时间; new Trigger().endAt():表示触发器结束触发的时间;
Date startDate = new Date(); startDate.setTime(startDate.getTime() + 5000); Date endDate = new Date(); endDate.setTime(startDate.getTime() + 5000); Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "这是jobDetail1的trigger") .startNow()//当即生效 .startAt(startDate) .endAt(endDate) .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1)//每隔1s执行一次 .repeatForever()).build();//一直执行
CronTrigger
CronTrigger功能很是强大,是基于日历的做业调度,而SimpleTrigger是精准指定间隔,因此相比SimpleTrigger,CroTrigger更加经常使用。CroTrigger是基于Cron表达式的,先了解下Cron表达式:
由7个子表达式组成字符串的,格式以下:
[秒] [分] [小时] [日] [月] [周] [年]
Cron表达式的语法比较复杂,
如:* 30 10 ? * 1/5 *
表示(从后往前看)
[指定年份] 的[ 周一到周五][指定月][不指定日][上午10时][30分][指定秒]
又如:00 00 00 ? * 10,11,12 1#5 2018
表示2018年十、十一、12月的第一周的星期五这一天的0时0分0秒去执行任务。
下面是给的一个例子:
可经过在线生成Cron表达式的工具:http://cron.qqe2.com/ 来生成本身想要的表达式。
下面的代码就实现了每周一到周五上午10:30执行定时任务
package com.todaytech.GdsdFs.QuartzDemo.Demo2; import java.util.Date; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; /** * 下面的代码就实现了每周一到周五上午10:30执行定时任务 * @author admin * */ public class MyScheduler2 { public static void main(String[] args) throws SchedulerException, InterruptedException { // 一、建立调度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 二、建立JobDetail实例,并与PrintWordsJob类绑定(Job执行内容) JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class) .usingJobData("jobDetail1", "这个Job用来测试的") .withIdentity("job1", "group1").build(); // 三、构建Trigger实例,每隔1s执行一次 Date startDate = new Date(); startDate.setTime(startDate.getTime() + 5000); Date endDate = new Date(); endDate.setTime(startDate.getTime() + 5000); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1") .usingJobData("trigger1", "这是jobDetail1的trigger") .startNow()//当即生效 .startAt(startDate) .endAt(endDate) .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018")) .build(); //四、执行 scheduler.scheduleJob(jobDetail, cronTrigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); System.out.println("--------scheduler shutdown ! ------------"); } }