Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。

这里使用的jar 包为:

slf4j-api-1.5.6.jar
slf4j-log4j12-1.5.2.jar
log4j-1.2.15.jar
jta.jar
quartz-1.6.0.jar

为了便于定时任务的添加,我们可以将定时任务放入数据库中

DROP TABLE IF EXISTS `sys_scheduler`;

CREATE TABLE `sys_scheduler` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cron` varchar(255) NOT NULL DEFAULT '',
`job_name` varchar(255) DEFAULT NULL,
`job_class` varchar(255) NOT NULL DEFAULT '',
`modify_time` datetime DEFAULT NULL,
`is_start` char(1) DEFAULT '0' COMMENT '任务状态是否启动,1-启动中,0-未启动',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=102 DEFAULT CHARSET=utf8;

/*Data for the table `sys_scheduler` */

insert into `sys_scheduler`(`id`,`cron`,`job_name`,`job_class`,`modify_time`,`is_start`) values (1,'0 30 * * * ?','示例定时任务','com.common.job.ExampleJob','2016-05-06 16:42:14','0');

关于如何去查询该表中的数据,此处省略

使用定时任务的方法:

package com.common.utils;

import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;

import com.common.dao.SysSchedulerDao;
import com.common.model.SysScheduler;

/**
* 定时任务 管理类
* @author ppnie
*
*/
public class SchedulerManager
{
static SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
static Scheduler sched = null;
static SchedulerManager schedulerMgr = null;

private static final Logger logger = Logger.getLogger(SchedulerManager.class.getName());

/**
* 创建定时任务单例,并且初始化启动定时任务
* @return
*/
public static SchedulerManager getInstance()
{
if (schedulerMgr == null)
{
schedulerMgr = new SchedulerManager();
schedulerMgr.init();
}
return schedulerMgr;
}

/**
* 初始化定时任务
*/
public void init()
{
try
{
sched = schedFact.getScheduler();
}
catch (Exception e)
{
logger.error("init1:" + e.getMessage());
}


start();

/*从数据库启动定时任务 */
SysSchedulerDao ssdao = new SysSchedulerDao();
List<SysScheduler> cdLists = ssdao.getSchedulerListByStart(ssdao.need_start);
Iterator<SysScheduler> ir = cdLists.iterator();
// System.out.println("-------------------Init Scheduler------------------");
while (ir.hasNext())
{
SysScheduler scheduler = ir.next();
scheduleJob(scheduler);
}

}

public void scheduleJob(SysScheduler scheduler)
{
scheduleJob(""+scheduler.getId(),scheduler.getJobClass(),scheduler.getCron());
if (isStarted())
{
System.out.println("Schedule " + scheduler.getJobName()+" class="+scheduler.getJobClass() + " cron=" + scheduler.getCron());
}
}

/**
* 添加某个任务到任务调度器
* @param jobId 任务id
* @param jobClass 任务job 类名
* @param cron 定时时间
* 设置定时器调度的时程表格式 秒 分 时 天 月 周 0 0 12 * * ? 每天中午十二点触发 0 15 10 15 * ? 每月15号的10:15触发 0 15 10 ? * 2-6
* 每个周一、周二、周三、周四、周五的10:15触发
*/
public void scheduleJob(String jobId, String jobClass, String cron)
{
if (isStarted())
{
try
{
Class cls = Class.forName(jobClass);

JobDetail jobDetail = new JobDetail(jobId, Scheduler.DEFAULT_GROUP, cls);

CronTrigger trigger = new CronTrigger(jobId, Scheduler.DEFAULT_GROUP, cron);

sched.scheduleJob(jobDetail, trigger);
}
catch (Exception e)
{
System.out.println("scheduleJob这里报错"+jobClass);
logger.error("scheduleJob:" + e.getMessage());
}
}
}

/**
*
* @param jobId
* @param jobClass
* @param cron
* @param data_map
*/
public void scheduleJob(String jobId, String jobClass, String cron, String data_map)
{
if (isStarted())
{
System.out.println("Schedule " + jobClass + " cron=" + cron);
try
{
Class cls = Class.forName(jobClass);

JobDetail jobDetail = new JobDetail(jobId, Scheduler.DEFAULT_GROUP, cls);

CronTrigger trigger = new CronTrigger(jobId, Scheduler.DEFAULT_GROUP, cron);

sched.scheduleJob(jobDetail, trigger);
}
catch (Exception e)
{
logger.error("scheduleJob:" + e.getMessage());
}
}
}

/**
* 根据jobId 在 任务调度器 中删除某个任务
* @param jobId
*/
public void delJob(String jobId)
{
try
{
sched.deleteJob(jobId, Scheduler.DEFAULT_GROUP);
}
catch (Exception e)
{
logger.error("deleteJob:" + e.getMessage());
}
}

/**
* 重新发布任务,在这里是根据jobId 修改任务执行时间 corn
* @param jobId
* @param cron
*/
public void rescheduleJob(String jobId, String cron)
{
try
{
JobDetail jd = sched.getJobDetail(jobId, Scheduler.DEFAULT_GROUP);
JobDataMap jdm = jd.getJobDataMap();
CronTrigger trigger = new CronTrigger("" + jobId, Scheduler.DEFAULT_GROUP, cron);
trigger.setJobName("" + jobId);
trigger.setJobGroup(Scheduler.DEFAULT_GROUP);
trigger.setJobDataMap(jdm);
sched.rescheduleJob(jobId, Scheduler.DEFAULT_GROUP, trigger);
}
catch (Exception e)
{
logger.error("rescheduleJob:" + e);
}
}

/**
* 启动 任务调度器
* 只有在Scheduler 有实例或standby 模式才能调用start() 方法
*/
public void start()
{
try
{
sched.start();
}
catch (Exception e)
{
logger.error("start:" + e);
}
}

/**
* 关闭 任务调度器Scheduler
* 一旦调用shutdown() 方法之后就不能在调用start() 方法
*/
public void shutdown()
{
System.out.println("------------------Shutdown Scheduler------------------");
try
{
sched.shutdown();
}
catch (Exception e)
{
logger.error("shutdown:" + e.getMessage());
}
schedulerMgr = null;
}

/**
* 判断 任务调度器Scheduler 是否被启动
* @return
*/
public boolean isStarted()
{
boolean re = false;
try
{
re = sched.isStarted();
}
catch (Exception e)
{
logger.error("isStarted:" + e.getMessage());
}
return re;
}

/**
* 判断 任务调度器Scheduler 是否关闭
* @return
* @throws SchedulerException
*/
public boolean isShutdown()
{
boolean re = false;
try
{
sched.isShutdown();
}
catch (SchedulerException e)
{
logger.error("isShutdown:" + e.getMessage());
}

return re;
}

/**
* 设置 Scheduler 为 standby 模式
* standby 模式时 Scheduler 暂时停止查找 Job 去执行
*/
public void standby() {
System.out.println("------------------Standby Scheduler------------------");
try {
sched.standby();
}
catch (Exception e) {
logger.error("standby:" + e.getMessage());
}
}

/**
* 判断Scheduler 操作是否是 standby 模式
*/
public boolean isInStandbyMode() {
boolean re = false;
try {
re = sched.isInStandbyMode();
} catch (Exception e) {
logger.error("isInStandbyMode:" + e.getMessage());
}
return re;
}

/**
* 判断任务是否存在
* @param jobId
* @return
*/
public boolean checkExists(String jobId)
{
boolean re = false;
try
{
CronTrigger trigger = (CronTrigger)sched.getTrigger("" + jobId, Scheduler.DEFAULT_GROUP);
return trigger != null;
}
catch (SchedulerException e)
{
logger.error("checkExists:" + e.getMessage());
}

return re;
}

}

写了定时器的管理方法后,定义一个定时任务

package com.common.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.common.utils.DateUtils;

public class ExampleJob implements Job
{

@Override
public void execute(JobExecutionContext arg0)
throws JobExecutionException
{
System.out.println(DateUtils.getDateTime()+" ExampleJob is executing...");

}

}

关于 DateUtils 方法此处也省略,就是获取当前时间

如果是web 项目,可以直接将定时管理放入servlet 中初始化,然后让servlet 在项目启动时启动,项目停止时关闭

在web.xml 中添加(类似)

class>com.common.servlet.SysSchedulerServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>

定时任务的相关方法解析:

   1 、JobDetail : JobDetail 是一个具体的类。

   2、Trigger :触发器,它用于定义 Job 何时执行。最常用的是 SimpleTrigger 和 CronTrigger 。一般来说,如果你需要在一个固定的时间和重复次数或者一个固定的间隔时间,那么 SimpleTrigger 比较合适;如果你有许多复杂的作业调度,那么 CronTrigger 比较合适。CronTrigger 和 Unix 的 cron 机制基本一样,我们需要的只是一个 cron 表达式。比如“ 0 0 12 * * ? ”会在每天中午 12 点触发 执行;“ 0 15 10 ? * 6L ”会在每个月的最后一个星期五的早上 10:15 触发 Job 执行。

   3、 Scheduler 和 SchedulerFactory : Scheduler 负责管理 Trigger 、调度 Job , SchedulerFactory 则是 Scheduler 工厂,负责生成Scheduler 。

 

 

字段名   允许的值   允许的特殊字符

​秒 ​

 

​0-59 ​

 

​, - * / ​

​分 ​

 

​0-59 ​

 

​, - * / ​

​小时 ​

 

​0-23 ​

 

​, - * / ​

​日 ​

 

​1-31 ​

 

​, - * ? / L W C ​

​月 ​

 

​1-12 or JAN-DEC ​

 

​, - * / ​

​周几 ​

 

​1-7 or SUN-SAT ​

 

​, - * ? / L C # ​

​年 (可选字段) ​

 

​empty, 1970-2099 ​

 

​, - * / ​

 

'*' 字符可以用于所有字段,在“分”字段中设为"*"表示"每一分钟"的含义。

'?' 字符可以用在“日”和“周几”字段. 它用来指定 '不明确的值'. 这在你需要指定这两个字段中的某一个值而不是另外一个的时候会被用到。在后面的例子中可以看到其含义。

'-' 字符被用来指定一个值的范围,比如在“小时”字段中设为"10-12"表示"10点到12点".

',' 字符指定数个值。比如在“周几”字段中设为"MON,WED,FRI"表示"the days Monday, Wednesday, and Friday".

'/' 字符用来指定一个值的的增加幅度. 比如在“秒”字段中设置为"0/15"表示"第0, 15, 30, 和 45秒"。而 "5/15"则表示"第5, 20, 35, 和 50". 在'/'前加"*"字符相当于指定从0秒开始. 每个字段都有一系列可以开始或结束的数值。对于“秒”和“分”字段来说,其数值范围为0到59,对于“小时”字段来说其为0到23, 对于“日”字段来说为0到31, 而对于“月”字段来说为1到12。"/"字段仅仅只是帮助你在允许的数值范围内从开始"第n"的值。 因此对于“月”字段来说"7/6"只是表示7月被开启而不是“每六个月”, 请注意其中微妙的差别。

'L'字符可用在“日”和“周几”这两个字段。它是"last"的缩写, 但是在这两个字段中有不同的含义。例如,“日”字段中的"L"表示"一个月中的最后一天" —— 对于一月就是31号对于二月来说就是28号(非闰年)。而在“周几”字段中, 它简单的表示"7" or "SAT",但是如果在“周几”字段中使用时跟在某个数字之后, 它表示"该月最后一个星期×" —— 比如"6L"表示"该月最后一个周五"。当使用'L'选项时,指定确定的列表或者范围非常重要,否则你会被结果搞糊涂的。

'W' 可用于“日”字段。用来指定历给定日期最近的工作日(周一到周五) 。比如你将“日”字段设为"15W",意为: "离该月15号最近的工作日"。因此如果15号为周六,触发器会在14号即周五调用。如果15号为周日, 触发器会在16号也就是周一触发。如果15号为周二,那么当天就会触发。然而如果你将“日”字段设为"1W", 而一号又是周六, 触发器会于下周一也就是当月的3号触发,因为它不会越过当月的值的范围边界。'W'字符只能用于“日”字段的值为单独的一天而不是一系列值的时候。

'L'和'W'可以组合用于“日”字段表示为'LW',意为"该月最后一个工作日"。

'#' 字符可用于“周几”字段。该字符表示“该月第几个周×”,比如"6#3"表示该月第三个周五( 6表示周五而"#3"该月第三个)。再比如: "2#1" = 表示该月第一个周一而 "4#5" = 该月第五个周三。注意如果你指定"#5"该月没有第五个“周×”,该月是不会触发的。

'C' 字符可用于“日”和“周几”字段,它是"calendar"的缩写。它表示为基于相关的日历所计算出的值(如果有的话)。如果没有关联的日历, 那它等同于包含全部日历。“日”字段值为"5C"表示"日历中的第一天或者5号以后",“周几”字段值为"1C"则表示"日历中的第一天或者周日以后"。

对于“月份”字段和“周几”字段来说合法的字符都不是大小写敏感的。

下面是一些完整的例子:

表达式   含义

​"0 0 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:55分结束每5分钟一次触发

​"0 0/5 14,18 * * ?" ​

 

每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发

​"0 0-5 14 * * ?" ​

 

每天14:00至14:05每分钟一次触发

​"0 10,44 14 ? 3 WED" ​

 

三月的每周三的14:10和14: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" ​

 

每月最后一个周五的10:15触发

​"0 15 10 ? * 6L 2002-2005" ​

 

2002年至2005年的每月最后一个周五的10:15触发

​"0 15 10 ? * 6#3" ​

 

每月的第三个周五的10:15触发

作者:panie