Cron-Expressions are used to configure instances of CronTrigger.
Cron-Expressions are strings that are actually made up of seven sub-expressions, that describe individual details of the schedule. These sub-expression are separated with white-space, and represent:
- Seconds
- Minutes
- Hours
- Day-of-Month
- Month
- Day-of-Week
- Year (optional field)
/ 用于递增触发。
如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。
在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。
quartz 2.0.2不支持*/5这种形式
The ‘/’ character can be used to specify increments to values. For example, if you put ‘0/15’ in the Minutes field, it means ‘every 15th minute of the hour, starting at minute zero’. If you used ‘3/20’ in the Minutes field, it would mean ‘every 20th minute of the hour, starting at minute three’ - or in other words it is the same as specifying ‘3,23,43’ in the Minutes field. Note the subtlety that “/35” does *not mean “every 35 minutes” - it mean “every 35th minute of the hour, starting at minute zero” - or in other words the same as specifying ‘0,35’.
The ‘?’ character is allowed for the day-of-month and day-of-week fields. It is used to specify “no specific value”. This is useful when you need to specify something in one of the two fields, but not the other. See the examples below (and CronTrigger JavaDoc) for clarification.
http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-06.html
调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不同的misfire对应的处理规则
CronTrigger
withMisfireHandlingInstructionDoNothing
——不触发立即执行
——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期后
——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
withMisfireHandlingInstructionFireAndProceed
——以当前时间为触发频率立刻触发一次执行
——然后按照Cron频率依次执行
SimpleTrigger
withMisfireHandlingInstructionFireNow
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期
——当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率
——共执行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithExistingCount
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionNextWithRemainingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithRemainingCount
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
——此指令导致trigger忘记原始设置的starttime和repeat-count
——触发器的repeat-count将被设置为剩余的次数
——这样会导致后面无法获得原始设定的starttime和repeat-count值
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。
Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的日程序表。Jobs可以做成标准的Java组件或 EJBs。
官方网站:http://www.opensymphony.com/quartz
相关Jar:
quartz-all-1.6.0.jar
jta.jar
commons-logging-1.1.jar
commons-collections3.2.jar
经过封装的管理类:
import java.text.ParseException; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; /** * 定时任务管理类 * * @author 王德封 */ public class QuartzManager { private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory(); private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME"; private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME"; /** * 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名 * * @param jobName * 任务名 * @param jobClass * 任务 * @param time * 时间设置,参考quartz说明文档 * @throws SchedulerException * @throws ParseException */ public static void addJob(String jobName, String jobClass, String time) { try { Scheduler sched = gSchedulerFactory.getScheduler(); JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, Class.forName(jobClass));// 任务名,任务组,任务执行类 // 触发器 CronTrigger trigger = new CronTrigger(jobName, TRIGGER_GROUP_NAME);// 触发器名,触发器组 trigger.setCronExpression(time);// 触发器时间设定 sched.scheduleJob(jobDetail, trigger); // 启动 if (!sched.isShutdown()){ sched.start(); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 添加一个定时任务 * * @param jobName * 任务名 * @param jobGroupName * 任务组名 * @param triggerName * 触发器名 * @param triggerGroupName * 触发器组名 * @param jobClass * 任务 * @param time * 时间设置,参考quartz说明文档 * @throws SchedulerException * @throws ParseException */ public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String jobClass, String time){ try { Scheduler sched = gSchedulerFactory.getScheduler(); JobDetail jobDetail = new JobDetail(jobName, jobGroupName, Class.forName(jobClass));// 任务名,任务组,任务执行类 // 触发器 CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组 trigger.setCronExpression(time);// 触发器时间设定 sched.scheduleJob(jobDetail, trigger); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名) * * @param jobName * @param time */ public static void modifyJobTime(String jobName, String time) { try { Scheduler sched = gSchedulerFactory.getScheduler(); CronTrigger trigger = (CronTrigger) sched.getTrigger(jobName, TRIGGER_GROUP_NAME); if(trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(time)) { JobDetail jobDetail = sched.getJobDetail(jobName, JOB_GROUP_NAME); Class objJobClass = jobDetail.getJobClass(); String jobClass = objJobClass.getName(); removeJob(jobName); addJob(jobName, jobClass, time); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 修改一个任务的触发时间 * * @param triggerName * @param triggerGroupName * @param time */ public static void modifyJobTime(String triggerName, String triggerGroupName, String time) { try { Scheduler sched = gSchedulerFactory.getScheduler(); CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerName, triggerGroupName); if(trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(time)) { CronTrigger ct = (CronTrigger) trigger; // 修改时间 ct.setCronExpression(time); // 重启触发器 sched.resumeTrigger(triggerName, triggerGroupName); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 移除一个任务(使用默认的任务组名,触发器名,触发器组名) * * @param jobName */ public static void removeJob(String jobName) { try { Scheduler sched = gSchedulerFactory.getScheduler(); sched.pauseTrigger(jobName, TRIGGER_GROUP_NAME);// 停止触发器 sched.unscheduleJob(jobName, TRIGGER_GROUP_NAME);// 移除触发器 sched.deleteJob(jobName, JOB_GROUP_NAME);// 删除任务 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 移除一个任务 * * @param jobName * @param jobGroupName * @param triggerName * @param triggerGroupName */ public static void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) { try { Scheduler sched = gSchedulerFactory.getScheduler(); sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器 sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器 sched.deleteJob(jobName, jobGroupName);// 删除任务 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 启动所有定时任务 */ public static void startJobs() { try { Scheduler sched = gSchedulerFactory.getScheduler(); sched.start(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 关闭所有定时任务 */ public static void shutdownJobs() { try { Scheduler sched = gSchedulerFactory.getScheduler(); if(!sched.isShutdown()) { sched.shutdown(); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } }
改造Quartz的JobRunShell类的202行代码、使定时任务支持数据库全程事务以及数据库连接的关闭:
- // execute the job
- try {
- log.debug("Calling execute on job " + jobDetail.getFullName());
- job.execute(jec);
- // 2011/1/22 王德封 添加
- DBUtil.commit();
- endTime = System.currentTimeMillis();
- } catch (JobExecutionException jee) {
- endTime = System.currentTimeMillis();
- jobExEx = jee;
- // 2011/1/22 王德封 添加
- DBUtil.rollback();
- getLog().info("Job " + jobDetail.getFullName() +
- " threw a JobExecutionException: ", jobExEx);
- } catch (Throwable e) {
- endTime = System.currentTimeMillis();
- getLog().error("Job " + jobDetail.getFullName() +
- " threw an unhandled Exception: ", e);
- SchedulerException se = new SchedulerException(
- "Job threw an unhandled exception.", e);
- se.setErrorCode(SchedulerException.ERR_JOB_EXECUTION_THREW_EXCEPTION);
- qs.notifySchedulerListenersError("Job ("
- + jec.getJobDetail().getFullName()
- + " threw an exception.", se);
- jobExEx = new JobExecutionException(se, false);
- jobExEx.setErrorCode(JobExecutionException.ERR_JOB_EXECUTION_THREW_EXCEPTION);
- // 2011/1/22 王德封 添加
- DBUtil.rollback();
- } finally {
- // 2011/1/22 王德封 添加
- DBUtil.closeCurrentConnection();
- }
测试代码:
- System.out.println("【系统启动】开始(每1秒输出一次)...");
- QuartzManager.addJob(job_name, job, "0/1 * * * * ?");
- //QuartzManager.addJob(job_name, job, "0 0/3 8-20 ? ? *");
- Thread.sleep(5000);
- System.out.println("【修改时间】开始(每2秒输出一次)...");
- QuartzManager.modifyJobTime(job_name, "10/2 * * * * ?");
- Thread.sleep(6000);
- System.out.println("【移除定时】开始...");
- QuartzManager.removeJob(job_name);
- System.out.println("【移除定时】成功");
- System.out.println("/n【再次添加定时任务】开始(每10秒输出一次)...");
- QuartzManager.addJob(job_name, job, "*/10 * * * * ?");
- Thread.sleep(60000);
- System.out.println("【移除定时】开始...");
- QuartzManager.removeJob(job_name);
- System.out.println("【移除定时】成功");
- package com.extjweb.quartz;
- import java.util.Calendar;
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- public class TestJob implements Job {
- @SuppressWarnings("deprecation")
- public void execute(JobExecutionContext arg0) throws JobExecutionException {
- System.out.println(Calendar.getInstance().getTime().toLocaleString()+ "★★★★★★★★★★★");
- }
- }
quartz 时间配置规则
格式: [秒] [分] [小时] [日] [月] [周] [年]
序号 | 说明 | 是否必填 | 允许填写的值 | 允许的通配符 |
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 小时 | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1-31 | , - * ? / L W |
5 | 月 | 是 | 1-12 or JAN-DEC | , - * / |
6 | 周 | 是 | 1-7 or SUN-SAT | , - * ? / L # |
7 | 年 | 否 | empty 或 1970-2099 | , - * / |
通配符说明:
* 表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。
? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
- 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。
, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发
/ 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。
L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-").
小提示 |
'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资 |
# 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)
小提示 |
周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同. |
常用示例:
0 0 12 * * ? | 每天12点触发 |
0 15 10 ? * * | 每天10点15分触发 |
0 15 10 * * ? | 每天10点15分触发 |
0 15 10 * * ? * | 每天10点15分触发 |
0 15 10 * * ? 2005 | 2005年每天10点15分触发 |
0 * 14 * * ? | 每天下午的 2点到2点59分每分触发 |
0 0/5 14 * * ? | 每天下午的 2点到2点59分(整点开始,每隔5分触发) |
0 0/5 14,18 * * ? |
每天下午的 2点到2点59分(整点开始,每隔5分触发) 每天下午的 18点到18点59分(整点开始,每隔5分触发) |
0 0-5 14 * * ? | 每天下午的 2点到2点05分每分触发 |
0 10,44 14 ? 3 WED | 3月分每周三下午的 2点10分和2点44分触发 |
0 15 10 ? * MON-FRI | 从周一到周五每天上午的10点15分触发 |
0 15 10 15 * ? | 每月15号上午10点15分触发 |
0 15 10 L * ? | 每月最后一天的10点15分触发 |
0 15 10 ? * 6L | 每月最后一周的星期五的10点15分触发 |
0 15 10 ? * 6L 2002-2005 | 从2002年到2005年每月最后一周的星期五的10点15分触发 |
0 15 10 ? * 6#3 | 每月的第三周的星期五开始触发 |
0 0 12 1/5 * ? | 每月的第一个中午开始每隔5天触发一次 |
0 11 11 11 11 ? | 每年的11月11号 11点11分触发(光棍节) |
注:这里使用的是Quartz1.6.5版本(包:quartz-1.6.5.jar)
- //<a href="http://lib.csdn.net/base/softwaretest" class='replace_word' title="软件测试知识库" target='_blank' style='color:#df3434; font-weight:bold;'>测试</a>main函数
- //QuartzTest.<a href="http://lib.csdn.net/base/javase" class='replace_word' title="Java SE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a>
- package quartzPackage;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public class QuartzTest {
- /** *//**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SimpleDateFormat DateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
- Date d = new Date();
- String returnstr = DateFormat.format(d);
- TestJob job = new TestJob();
- String job_name ="11";
- try {
- System.out.println(returnstr+ "【系统启动】");
- QuartzManager.addJob(job_name,job,"0/2 * * * * ?"); //每2秒钟执行一次
- // Thread.sleep(10000);
- // System.out.println("【修改时间】");
- // QuartzManager.modifyJobTime(job_name,"0/10 * * * * ?");
- // Thread.sleep(20000);
- // System.out.println("【移除定时】");
- // QuartzManager.removeJob(job_name);
- // Thread.sleep(10000);
- //
- // System.out.println("/n【添加定时任务】");
- // QuartzManager.addJob(job_name,job,"0/5 * * * * ?");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
简单的任务管理类
- //简单的任务管理类
- //QuartzManager.java
- package quartzPackage;
- import java.text.ParseException;
- import org.quartz.CronTrigger;
- import org.quartz.Job;
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerException;
- import org.quartz.SchedulerFactory;
- import org.quartz.Trigger;
- import org.quartz.impl.StdSchedulerFactory;
- /** *//**
- * @Title:Quartz管理类
- *
- * @Description:
- *
- * @Copyright:
- * @author zz 2008-10-8 14:19:01
- * @version 1.00.000
- *
- */
- public class QuartzManager {
- private static SchedulerFactory sf = new StdSchedulerFactory();
- private static String JOB_GROUP_NAME = "group1";
- private static String TRIGGER_GROUP_NAME = "trigger1";
- /** *//**
- * 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
- * @param jobName 任务名
- * @param job 任务
- * @param time 时间设置,参考quartz说明文档
- * @throws SchedulerException
- * @throws ParseException
- */
- public static void addJob(String jobName,Job job,String time)
- throws SchedulerException, ParseException{
- Scheduler sched = sf.getScheduler();
- JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, job.getClass());//任务名,任务组,任务执行类
- //触发器
- CronTrigger trigger =
- new CronTrigger(jobName, TRIGGER_GROUP_NAME);//触发器名,触发器组
- trigger.setCronExpression(time);//触发器时间设定
- sched.scheduleJob(jobDetail,trigger);
- //启动
- if(!sched.isShutdown())
- sched.start();
- }
- /** *//**
- * 添加一个定时任务
- * @param jobName 任务名
- * @param jobGroupName 任务组名
- * @param triggerName 触发器名
- * @param triggerGroupName 触发器组名
- * @param job 任务
- * @param time 时间设置,参考quartz说明文档
- * @throws SchedulerException
- * @throws ParseException
- */
- public static void addJob(String jobName,String jobGroupName,
- String triggerName,String triggerGroupName,
- Job job,String time)
- throws SchedulerException, ParseException{
- Scheduler sched = sf.getScheduler();
- JobDetail jobDetail = new JobDetail(jobName, jobGroupName, job.getClass());//任务名,任务组,任务执行类
- //触发器
- CronTrigger trigger =
- new CronTrigger(triggerName, triggerGroupName);//触发器名,触发器组
- trigger.setCronExpression(time);//触发器时间设定
- sched.scheduleJob(jobDetail,trigger);
- if(!sched.isShutdown())
- sched.start();
- }
- /** *//**
- * 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
- * @param jobName
- * @param time
- * @throws SchedulerException
- * @throws ParseException
- */
- public static void modifyJobTime(String jobName,String time)
- throws SchedulerException, ParseException{
- Scheduler sched = sf.getScheduler();
- Trigger trigger = sched.getTrigger(jobName,TRIGGER_GROUP_NAME);
- if(trigger != null){
- CronTrigger ct = (CronTrigger)trigger;
- ct.setCronExpression(time);
- sched.resumeTrigger(jobName,TRIGGER_GROUP_NAME);
- }
- }
- /** *//**
- * 修改一个任务的触发时间
- * @param triggerName
- * @param triggerGroupName
- * @param time
- * @throws SchedulerException
- * @throws ParseException
- */
- public static void modifyJobTime(String triggerName,String triggerGroupName,
- String time)
- throws SchedulerException, ParseException{
- Scheduler sched = sf.getScheduler();
- Trigger trigger = sched.getTrigger(triggerName,triggerGroupName);
- if(trigger != null){
- CronTrigger ct = (CronTrigger)trigger;
- //修改时间
- ct.setCronExpression(time);
- //重启触发器
- sched.resumeTrigger(triggerName,triggerGroupName);
- }
- }
- /** *//**
- * 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
- * @param jobName
- * @throws SchedulerException
- */
- public static void removeJob(String jobName)
- throws SchedulerException{
- Scheduler sched = sf.getScheduler();
- sched.pauseTrigger(jobName,TRIGGER_GROUP_NAME);//停止触发器
- sched.unscheduleJob(jobName,TRIGGER_GROUP_NAME);//移除触发器
- sched.deleteJob(jobName,JOB_GROUP_NAME);//删除任务
- }
- /** *//**
- * 移除一个任务
- * @param jobName
- * @param jobGroupName
- * @param triggerName
- * @param triggerGroupName
- * @throws SchedulerException
- */
- public static void removeJob(String jobName,String jobGroupName,
- String triggerName,String triggerGroupName)
- throws SchedulerException{
- Scheduler sched = sf.getScheduler();
- sched.pauseTrigger(triggerName,triggerGroupName);//停止触发器
- sched.unscheduleJob(triggerName,triggerGroupName);//移除触发器
- sched.deleteJob(jobName,jobGroupName);//删除任务
- }
- }
测试工作类
- //测试工作类
- //TestJob.java
- package quartzPackage;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- public class TestJob implements Job {
- SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Date d = new Date();
- String returnstr = DateFormat.format(d);
- public void execute(JobExecutionContext arg0) throws JobExecutionException {
- // TODO Auto-generated method stub
- System.out.println(returnstr+"★★★★★★★★★★★");
- }
- }
第三方包
通过测试。一个简单任务只需要以下几个包:commons-beanutils.jar、commons-collections.jar、commons-logging.jar、commons-digester.jar、quartz.jar即可
名称 | 必须/备注 | 网址 |
activation.jar | 主要是 JavaMail 要用到 | http://java.sun.com/products/javabeans/glasgow/jaf.html |
commons-beanutils.jar | 是 | http://jakarta.apache.org/commons/beanutils |
commons-collections.jar | 是 | http://jakarta.apache.org/commons/collections |
commons-dbcp-1.1.jar | 是,假如用到数据库作为作业存储 | http://jakarta.apache.org/commons/dbcp |
commons-digester.jar | 是 | 假如你使用了某些插件,就需要它 |
commons-logging.jar | 是 | http://jakarta.apache.org/commons/logging/ |
commons-pool-1.1.jar | http://jakarta.apache.org/commons/pool/ | |
javamail.jar | 发送 e-mail 用 | http://java.sun.com/products/javamail/ |
jdbc2_0-stdext.jar | 是,假如用到数据库作为作业存储 | http://java.sun.com/products/jdbc/ |
jta.jar | 是,假如用到数据库作为作业存储 | http://java.sun.com/products/jta/database |
quartz.jar | 是 | Quart 框架核心包 |
servlet.jar | 假如使用了Servlet 容器,但容器中应该存在 | http://java.sun.com/products/servlet/ |
log4j.jar | 是,日志 | http://logging.apache.org/ |
http://blog.csdn.net/lotusyangjun/article/details/6450421
1.SimpleTrigger 一般用于实现每隔一定时间执行任务,以及重复多少次,如每 60 秒执行一次,重复执行 6 次。
问题: (1) 在使用过程中发现设置执行6次其实是执行7次, 有一次是在开始执行的第 0 秒执行了一次, 然后根据执行间隔再执行给定的执行次数。
(2) 当有 misfired 的任务并且恢复执行时,该执行时间是随机的(取决于何时执行 misfired 的任务,例如某天的 6:00PM)。这会导致之后每天的执行时间都会变成 6:00PM,而不是我们原来期望的时间。(PS: 因为这个问题, 我考虑不对外提供SimpleTrigger, 而换用DailyTimeIntervalTrigger)
2. CronTirgger 使用类似于 Linux/Unix 上的任务调度命令 crontab,具体参见Quartz入门详解的Cron表达式。对于涉及到星期和月份的调度,CronTirgger 是最适合的,甚至某些情况下是唯一选择。例如,"00 06 06 ? * WED 2014" 表示2014年每月每个星期三的 06:06AM 执行任务。
3. DailyTimeIntervalTrigger 会在给定的时间区间内每隔 N(1, 2, 3...)秒或小时执行任务。例如:设置从周一到周五10:10 ~ 18:00每60分钟执行一次。虽然 SimpleTrigger 也能实现类似的任务,但是DailyTimeIntervalTrigger 不会受到上面提到的 misfired 任务的问题。
4.CalendarIntervalTrigger 一般用于处理基于日历时间重复间隔任务。可以处理SimpleTrigger不能处理的任务(如:每个月的间隔秒数不同)和CronTrigger不能处理的任务(如:每5个月不是12的除数)。
除了上面提到的 4 种 Trigger,Quartz 中还定义了一个 Calendar 类(org.quartz.Calendar)。 Calendar 类与 Trigger 一起使用,它是用于排除任务不被执行的情况。例如,按照 Trigger 的规则在 5 月 1 号需要执行任务,但是 HolidayCalendar 指定了 5 月 1 号是法定假节日,所以任务在这一天将不会被执行。当然Calendar类不仅仅提供了节假日的排除, 还有Cron表达式排除等子类实现。
http://blog.csdn.net/wenniuwuren/article/details/42080293
入门简介:
基本上任何公司都会用到调度这个功能, 比如我们公司需要定期执行调度生成报表, 或者比如博客什么的定时更新之类的,都可以靠Quartz来完成。正如官网所说,小到独立应用大到大型电子商务网站, Quartz都能胜任。
Quartz体系结构:
明白Quartz怎么用,首先要了解Scheduler(调度器)、Job(任务)和Trigger(触发器)这3个核心的概念。
1. Job: 是一个接口,只定义一个方法execute(JobExecutionContext context),在实现接口的execute方法中编写所需要定时执行的Job(任务), JobExecutionContext类提供了调度应用的一些信息。Job运行时的信息保存在JobDataMap实例中;
2. JobDetail: Quartz每次调度Job时, 都重新创建一个Job实例, 所以它不直接接受一个Job的实例,相反它接收一个Job实现类(JobDetail:描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息),以便运行时通过newInstance()的反射机制实例化Job。
3. Trigger: 是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的15:00~16:00执行调度等;
Cron表达式的格式:秒 分 时 日 月 周 年(可选)。
字段名 允许的值 允许的特殊字符
秒 0-59 , – * /
分 0-59 , – * /
小时 0-23 , – * /
日 1-31 , – * ? / L W C
月 1-12 or JAN-DEC , – * /
周几 1-7 or SUN-SAT , – * ? / L C # MON FRI
年 (可选字段) empty, 1970-2099 , – * /
“?”字符:表示不确定的值
“,”字符:指定数个值
“-”字符:指定一个值的范围
“/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m
“L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X
“W”字符:指定离给定日期最近的工作日(周一到周五)
“#”字符:表示该月第几个周X。6#3表示该月第3个周五
Cron表达式范例:
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
每周星期天凌晨1点实行一次:0 0 1 ? * L
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
4. Calendar:org.quartz.Calendar和java.util.Calendar不同, 它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。 一个Trigger可以和多个Calendar关联, 以便排除或包含某些时间点。
假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。针对不同时间段类型,Quartz在org.quartz.impl.calendar包下提供了若干个Calendar的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义;
5. Scheduler: 代表一个Quartz的独立运行容器, Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
Scheduler可以将Trigger绑定到某一JobDetail中, 这样当Trigger触发时, 对应的Job就被执行。一个Job可以对应多个Trigger, 但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;
6. ThreadPool: Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。
正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job。
如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。
Trigger自身也可以拥有一个JobDataMap,其关联的Job可以通过JobExecutionContext#getTrigger().getJobDataMap()获取Trigger中的JobDataMap。不管是有状态还是无状态的任务,在任务执行期间对Trigger的JobDataMap所做的更改都不会进行持久,也即不会对下次的执行产生影响。
Quartz拥有完善的事件和监听体系,大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件。
下图描述了Scheduler的内部组件结构,SchedulerContext提供Scheduler全局可见的上下文信息,每一个任务都对应一个JobDataMap,虚线表达的JobDataMap表示对应有状态的任务:
废话不多说, 上代码:
1. 最简单的Job代码(就打印Hello Quartz !):
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.wenniuwuren.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloQuartz implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println( "Hello Quartz !" ); } } |
2. 设置触发器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
package com.wenniuwuren.quartz; import org.quartz.CronScheduleBuilder; 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; public class SchedulerTest { public static void main(String[] args) throws InterruptedException { //通过schedulerFactory获取一个调度器 SchedulerFactory schedulerfactory = new StdSchedulerFactory(); Scheduler scheduler= null ; try { // 通过schedulerFactory获取一个调度器 scheduler = schedulerfactory.getScheduler(); // 创建jobDetail实例,绑定Job实现类 // 指明job的名称,所在组的名称,以及绑定job类 JobDetail job = JobBuilder.newJob(HelloQuartz. class ).withIdentity( "JobName" , "JobGroupName" ).build(); // 定义调度触发规则 // SimpleTrigger // Trigger trigger=TriggerBuilder.newTrigger().withIdentity("SimpleTrigger", "SimpleTriggerGroup") // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3).withRepeatCount(6)) // .startNow().build(); // corn表达式 每五秒执行一次 Trigger trigger=TriggerBuilder.newTrigger().withIdentity( "CronTrigger1" , "CronTriggerGroup" ) .withSchedule(CronScheduleBuilder.cronSchedule( "*/5 * * * * ?" )) .startNow().build(); // 把作业和触发器注册到任务调度中 scheduler.scheduleJob(job, trigger); // 启动调度 scheduler.start(); Thread.sleep( 10000 ); // 停止调度 scheduler.shutdown(); } catch (SchedulerException e){ e.printStackTrace(); } } } |
输出(设置了sleep10秒, 故在0秒调度一次, 5秒一次, 10秒最后一次):
http://www.importnew.com/22896.html
import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { @Bean public JobDetail uploadTaskDetail() { return JobBuilder.newJob(RedisStockTask.class).withIdentity("RedisStockTask").storeDurably().build(); } @Bean public Trigger uploadTaskTrigger() { //CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/5 * * * * ?");//每5秒运行一次 //CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 0 0 * * ?");//每天0点运行一次 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 */3 * * * ?");//每3分运行一次 //CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 0 * * * ?");//每小时运行一次 return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()) .withIdentity("RedisStockTask") .withSchedule(scheduleBuilder) .build(); } }
import com.service.ActivityService; import lombok.extern.slf4j.Slf4j; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; import java.util.Date; @DisallowConcurrentExecution @Slf4j public class RedisStockTask extends QuartzJobBean { @Autowired private ActivityService activityService; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.info("========,注意,定时器开始运行==========" + new Date()); try { // activityService.setRedisStock(); } catch (Exception e) { e.printStackTrace(); } log.info("========,注意,定时器运行结束==========" + new Date()); } }
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>