任务调度框架Quartz

java.util.Timer vs Quartz

从调度的灵活度比较

Timer Quartz
从指定时间执行一次 Timer能做的都能做
从firstTime时刻开始,每隔period毫秒执行一次
从现在起过delay毫秒执行一次
从现在起过delay毫秒以后,每隔period毫秒执行一次 Timer不能做的也能做

从调度数据的存储方式比较

Timer Quartz
内存 内存&数据库

What is Quartz?

Quartz是由Java实现的开源任务调度框架。作为一个优秀的任务调度框架,必然具有卓越的特点:

  1. 强大的调度功能
    不但能够定时执行任务、周期执行任务,还能够在某一时间点执行任务。支持丰富多样的调度方法,满足各种常规及殊需求。
  2. 灵活的应用方式
    支持任务和调度的多种组合方式,支持调度数据的多种存储方式。
  3. 灵活的部署方式
    提供分布式和集群部署能力。
  4. Spring默认的调度框架
    不但在Spring中可以使用,在Jboss、JIRA、Jarkata等中都可以使用。

Hello, Quartz!

寥寥几行代码,展示Quartz的魅力。

public class HelloJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Date date = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
                    
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String para = jobDataMap.getString("para");
        System.out.println(para + " - " + dateStr);
}
          
    public static void main(String[] args) {
        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            String jobName = "HelloJob";
            String jobGroup = Scheduler.DEFAULT_GROUP;
                               
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity(jobName, jobGroup).build();
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            jobDataMap.put("para", "hello");//job执行的参数
                               
            //创建触发器:每5秒执行一次,共执行3+1次
            SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3);
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().withSchedule(builder).build();
                  
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();//启动scheduler
        } catch (SchedulerException e) {
            e.printStackTrace();
        }       
     }
}

运行结果:

hello - 2016-11-10 15:20:26
hello - 2016-11-10 15:20:31
hello - 2016-11-10 15:20:36
hello - 2016-11-10 15:20:41

核心元素

在上面的例子中,可以看出Quartz几个核心元素。
1.Scheduler
Scheduler是任务调度容器,其中可以存放若干对JobDetail和Trigger。当Scheduler启动后,JobDetail就会根据Trigger按部就班的自动执行。
在Quartz中,Scheduler由Scheduler工厂创建:StdSchedulerFactory或DirectSchedulerFactory。目前,StdSchedulerFactory使用较多。
在Quartz中,Scheduler有4种:JBoss4RMIRemoteMBeanScheduler、RemoteMBeanScheduler、RemoteScheduler、StdScheduler。其中,以StdScheduler最为常用。
创建Scheduler的方法如下:

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

2.Trigger
Trigger制定了一个时间规则,告诉作业什么时候可以工作。Quartz提供了8种Trigger:CalendarIntervalTrigger、CoreTrigger、CronTrigger、DailyTimeIntervalTrigger、LocalityTrigger、MutableTrigger、OperableTrigger、SimpleTrigger。其中,CronTrigger和SimpleTrigger最为常用,且CronTrigger最为灵活。

创建SimpleTrigger方法如下:

SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).startNow().withSchedule(builder).build();

创建CronTrigger方法如下:

String cron = "0 55 14 * * ?";
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按照cron表达式构建一个新的trigger

CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();

3.Job
Job是Trigger触发后,在触发时间点到达时,执行的具体内容。在Quartz中,主要有两种类型的Job:无状态(stateless)的Job和有状态(stateful)的Job。两者的区别是:对于有状态的Job,不能被并行执行,只能在上一次触发执行结束后,才能触发执行。
无论哪种Job,只需实现execute接口即可:

public class HelloJob **implements Job** {
    public void **execute**(JobExecutionContext context) throws JobExecutionException {
        ……
    }
}

4.JobDetail
Job是不能直接与Trigger结合的,只有通过JobDetail才能将Job和Trigger结合起来,并为Job设置属性、传递参数。

JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity(jobName, jobGroup).build();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.put("para", "hello");//job执行的参数
……
scheduler.scheduleJob(jobDetail, trigger);

Cron表达式

Quartz有两大触发器,除了在上面示例中使用的SimpleTrigger外,还有一个就是无所不能的CronTrigger。CronTrigger能够提供复杂的触发器表达式的支持,是基于Unix Cron守护进程,是一个调度程序,支持简单而强大的触发器语法。
而只要掌握了Cron表达式,就掌握了CronTrigger的精髓。Cron表达式包含7部分,其中:6个是必要组件,1个是可选组件。这7部分分别为:

秒 分 小时 日期 月 星期 年

下面详细介绍一下Cron中的每一部分:

位置 含义 特殊字符
1 秒(0-59) , - * /
2 分(0-59) , - * /
3 小时(0-24) , - * /
4 日期(1-31) , - * / ? L W C
5 月(JAN-DEC或1-12) , - * /
6 星期(SUN-SAT或1-7) , - * / ? L C #
7 年(可选,1970-2099) , - * /

每一部分又允许一些特殊字符,这些特殊字符的含义如下表:

特殊字符 含义
* 通配符,任意值
? 无特定值。通常和其他指定的值一起使用,表示必须显示该值但不能检查
- 范围。例如,小时部分10-12表示10:00,11:00, 12:00
, 列分隔符。可以指定一系列的值。例如,在星期域中指定MON、TUE和WED
/ 增量。例如,分钟域中0/1表示从0开始,每次增加1min
L 表示Last。在日期和星期域中表示有所不同。在日期域中,表示这个月的最后一天,而在星期域中,永远是7(星期六)。当希望使用星期中某一天时,L字符非常有用。例如,星期域中6L表示每一个月的最后一个星期五
W 在本月内离当天最近的工作日触发,所谓的最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离是0;所谓本月内指的是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日。例如,LW表示本月的最后一个工作日触发,W强烈依赖月份。
# 表示该月的第几个星期,例如,1#2表示每一个月的第一个星期一。
C 日历值。日期值是根据一个给定的日历计算出来的。在日期域中给定一个20C将在20日(日历包括20日)或20日后日历中包含的第一天(不包括20日)激活触发器。例如在一个星期域中使用6C表示日历中星期五(日历包括星期五)或者第一天(日历不包括星期五)。

每一部分又允许一些特殊字符,这些特殊字符的含义如下表:
理论太多,还是举一些例子:

时间表达式 含义
30 * * * * ? 每半分钟触发任务
30 10 * * * ? 每小时的10分30秒触发任务
30 10 1 * * ? 每天1点10分30秒触发任务
30 10 1 20 * ? 每月20号1点10分30秒触发任务
30 10 1 20 10 ? * 每年10月20号1点10分30秒触发任务
30 10 1 20 10 ? 2011 2011年10月20号1点10分30秒触发任务
30 10 1 ? 10 * 2011 2011年10月每天1点10分30秒触发任务
30 10 1 ? 10 SUN 2011 2011年10月每周日1点10分30秒触发任务
15,30,45 * * * * ? 每15秒,30秒,45秒时触发任务
15-45 * * * * ? 15到45秒内,每秒都触发任务
15/5 * * * * ? 每分钟的每15秒开始触发,每隔5秒触发一次
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
0 0/3 * * * ? 每小时的第0分0秒开始,每三分钟触发一次
0 15 10 ? * MON-FRI 星期一到星期五的10点15分0秒触发任务
0 15 10 L * ? 每个月最后一天的10点15分0秒触发任务
0 15 10 LW * ? 每个月最后一个工作日的10点15分0秒触发任务
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发任务
0 15 10 ? * 5#3 每个月第三周的星期四的10点15分0秒触发任务

说的再多,也不如一个实例看的明白。既然在开始举了一个SimpleTrigger的实例,接下来就为CronTrigger来一个实例。

public class CronJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String date = dataMap.getString("date");
        System.out.println("HelloJob - " + date);
    }
 
    public static void main(String[] args) {
        try {
            SchedulerFactory sf = new StdSchedulerFactory();
            Scheduler scheduler = sf.getScheduler();
            String jobName = "HelloJob";
            String jobGroup = Scheduler.DEFAULT_GROUP;
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(CronJob.class).withIdentity(jobName, jobGroup).build();
            String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            jobDetail.getJobDataMap().put("date", dateStr);
            // 每天下午2点55触发
            String cron = "0 55 14 * * ?";
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // 按cron表达式构建一个trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
 
            scheduler.scheduleJob(jobDetail, trigger);
                               
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
     }
}

你可能感兴趣的:(任务调度框架Quartz)