任务调度框架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();
        }
     }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容