系统开发中,经常需要用到周期性执行某个任务的需求,或者资源上的调度,给谁分配,谁等待问题,之前,一直是个菜鸟,只知道用sun提供了java.util.Timer和TimerTask的简单调度功能,今天知道了Quartz开源任务调度框架,并学习到了Spring关于任务调度的集成。一起来学习下吧。
Quartz是任务调度的开源框架,使用它,我们可以很容易的开发出系统中需要的任务调度模块,包括资源的调度,Quartz前后出现了许多版本,本文就 主要根据quartz最新版本2.2.1进行讲述,不同版本可能里面类的创建和实例化有所差异,使用时最好去看一下doc文档。
Quartz中有三个核心概念,调度器,任务,和触发器。有以下的核心类和接口。
Job | 接口,void execute(JobExecutionContext context)实现该接口定义需要执行的任务。jobExectutionContext类提供了调度上下文的各种信息,job运行时信息保存于JobDataMap实例中。 StatefulJob接口是Job子接口,表示有状态的任务。 |
JobDetail | JobDetail类承担了描述Job实现类和其他相关静态信息(Job名称、描述、关联监听器)。 JobDetail(String name ,String group,Class jobClass)要求指定Job实现类,任务在Scheduler中的组名和Job名称 |
Trigger | 描述Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger两个子类。SimpleTrigger负责仅触发一次或固定时间间隔周期性执行。CronTrigger可以通过Cron表达式定义出复杂的调度方案 |
Calendar | ogr.quartz.Calendar是一些日历特定时间点的集合,可以看做java,util.Calendar集合,一个Trigger可以与多个Calendar相关联 ,用于排除你或包含某些时间点。若干子类,AnnualCalendar、MonthlyCalendar、WeeklyCalendar |
Scheduler | 表示一个Quartz独立运行容器,Trigger和jobDetail可以注册到Scheduler中,在Scheduler中拥有各自的组和名称。组和名称是Scheduler查找定位容器内某一对象的依据。它将Trigger绑定到JobDetail中,Trigger触发,job就被执行。 Scheduler可以通过SchedulerFactory创建,通过Scheduler#getContext()获取SchedulerContext上下文信息,job和Trigger就可以访问这些信息了。 |
ThreadPool | Scheduler使用线程池作为任务运行的基础,任务通过共享线程池中的线程提高运行效率 |
import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleJob implements Job { //①实例Job接口方法 public void execute(JobExecutionContext jobCtx)throws JobExecutionException { System.out.println(jobCtx.getTrigger().getDescription()+ " triggered. time is:" + (new Date())); } }
import java.util.Date; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.JobDetailImpl; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.triggers.SimpleTriggerImpl; public class SimpleTriggerRunner { public static void main(String[] args) { try{ //①创建一个JobDetail实例,指定SimpleJob JobDetail jobDetail = new JobDetailImpl("job1_1","jGroup1", SimpleJob.class); //②通过SimpleTrigger定义调度规则:马上启动,每2秒运行一次,共运行100次 SimpleTriggerImpl simpleTrigger = new SimpleTriggerImpl("trigger1_1","tgroup1"); simpleTrigger.setStartTime(new Date()); simpleTrigger.setDescription("SimpleTriggerRunner"); simpleTrigger.setRepeatInterval(2000); simpleTrigger.setRepeatCount(100); //③通过SchedulerFactory获取一个调度器实例 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail, simpleTrigger); //④ 注册并进行调度 scheduler.start();//⑤调度启动 }catch (Exception e) { e.printStackTrace(); } } }
import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.JobDetailImpl; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.triggers.CronTriggerImpl; public class CronTriggerRunner { public static void main(String[] args) { try { JobDetailImpl jobDetail=new JobDetailImpl(); jobDetail.setName("job1_2"); jobDetail.setGroup("jGroup1"); jobDetail.setJobClass(SimpleJob.class); CronTriggerImpl cronTrigger=new CronTriggerImpl(); cronTrigger.setName("trigger1_2"); cronTrigger.setGroup("tgroup1"); cronTrigger.setDescription("cronTrgger"); cronTrigger.setCronExpression("0/5 1 * * * ?"); SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail, cronTrigger); scheduler.start(); } catch (Exception e) { // TODO: handle exception } } }
import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import org.quartz.DateBuilder; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SimpleTrigger; import org.quartz.impl.JobDetailImpl; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.calendar.AnnualCalendar; import org.quartz.impl.triggers.SimpleTriggerImpl; public class CalendarExample { public static void main(String[] args) { try{ SchedulerFactory sf=new StdSchedulerFactory(); Scheduler scheduler=sf.getScheduler(); AnnualCalendar holidays=new AnnualCalendar(); Calendar laborDay=new GregorianCalendar(); laborDay.add(Calendar.MONTH, 5); laborDay.add(Calendar.DATE, 28); Calendar nationtialDay=new GregorianCalendar(); nationtialDay.add(Calendar.MONTH, 10); nationtialDay.add(Calendar.DATE, 1); ArrayList<Calendar> calendars=new ArrayList<Calendar>(); calendars.add(laborDay); calendars.add(nationtialDay); holidays.setDaysExcluded(calendars); scheduler.addCalendar("holidays", holidays, false, false); Date runDate=DateBuilder.dateOf(10, 0, 0, 1, 4); JobDetailImpl jobDetail=new JobDetailImpl(); jobDetail.setName("job1"); jobDetail.setGroup("group1"); jobDetail.setJobClass(SimpleJob.class); jobDetail.setDescription("Calendar job"); SimpleTriggerImpl simpleTrigger=new SimpleTriggerImpl(); simpleTrigger.setName("trigger1"); simpleTrigger.setGroup("group1"); simpleTrigger.setStartTime(runDate); simpleTrigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); simpleTrigger.setRepeatInterval(60L*1000L); simpleTrigger.setCalendarName("holidays"); scheduler.scheduleJob(jobDetail, simpleTrigger); scheduler.start(); }catch(Exception e){ } } }
注:如果以上看到的与你在其他例子上看到的有所差异是应为这是基于quartz最新包quartz-2.2.1-distribution.zip写的,编译通过的。
CronTrigger CronExpression详解:
Quartz利用linux下cron表达式定义时间规则,由6或7个空格分隔的时间字段组成
位置 | 时间域名 | 允许值 | 允许的特殊字符 |
1 | 秒 | 0-59 | ,- * / |
2 | 分钟 | 0-59 | ,- * / |
3 | 小时 | 0-23 | ,- * / |
4 | 日期 | 1-31 | ,- * ? / L W C |
5 | 月份 | 1-12 | ,- * / |
6 | 星期 | 1-7 | ,- * ? / L C # |
7 | 年(可选) | 空值 1970-2099 | ,- * / |
特殊字符介绍:
* 可用于所有字段,表示对应时间域的每一时刻
? 在日期和星期字段中使用,表示无意义的值,相当于占位符
- 表达一个范围,如小时字段 10- 12 表示10点到12点
,表示 一个列表值,如星期字段使用 MON,FRI,WED 表示星期一,星期五和星期三
/ x/y表示一个等步长序列,x表示起始值,y为增量步长值,分钟字段使用0/15 表示 0,15,30,45秒。*/y等同于0/y
L Last 在日期和星期字段中使用,6L在星期字段中表示这个月的最后星期五
W ,只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。15W 。离这个月15号最近的工作日,不能够夸月
LW 当月的最后一个工作日
# 只用在星期字段,表示当月的某个工作日。 6#3 当月的第三个星期五, 5#4当月的第4个星期四,如果没有第四个星期四则默认不触发
C 该字符只能用在日期和星期字段,代表Calendar。例如:5C在日期字段中相当于5日以后的第一天,在星期字段中相当于星期四后的第一天。
任务调度信息存储:
quartz将所有的任务调度的执行信息保存在内存中,这样做缺乏数据的持久性,Quartz允许用户调整其属性文件将任务调度的执行信息持久化到数据库中。
Quartz JAR包 ogr.quartz包下有个quartz.properties属性配置文件,其包含了1)集存信息,2)调度器线程池 3)任务调度现场数据的存储。
quartz.proeprteis默认配置
# Default Properties file for use by StdSchedulerFactory # to create a Quartz Scheduler Instance, if a different # properties file is not explicitly specified. # org.quartz.scheduler.instanceName: DefaultQuartzScheduler org.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false org.quartz.scheduler.wrapJobExecutionInUserTransaction: false org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount: 10 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true org.quartz.jobStore.misfireThreshold: 60000 org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
可以通过修改配置将任务调度现场保存至数据库中
org.quartz.jobStore.class: org.quartz.impl.jdbcJobStore.JobStoreTX
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.dataSource=qzDS
org.quartz.dataSource.qzDS.Driver=
org.quartz.dataSource.qzDS.URL=
org.quartz.dataSource.qzDS.user=
org.quartz.dataSource.qzDS.password=
org.quartz.dataSource.qzDS.maxConnections=
需要创建数据库,在doc/dbTables下有不同的数据库脚本,执行一遍即可。