zoukankan      html  css  js  c++  java
  • Quartz入门

    Quartz体系结构:

    明白Quartz怎么用,首先要了解Scheduler(调度器)、Job(任务)和Trigger(触发器)这3个核心的概念。请注意加粗内容。

    1. Job: 是一个接口,只定义一个方法execute(JobExecutionContext context),在实现接口的execute方法中编写所需要定时执行的Job(任务), JobExecutionContext类提供了调度应用的一些信息。Job运行时的信息保存在JobDataMap实例中,通过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            , – * /

    通配符详解:
      “*”字符 表示所有值,例如:在分的字段上设置 "*",表示每一分钟都会触发。
      “?”字符 表示不指定值,使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的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"前只能设置具体的数字,不允许区间"-")。
      “#” 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周五,注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;小提示:'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同。

    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中拥有各自的组(Group)及名称(Name), 组及名称是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拥有完善的事件和监听体系(Listener),大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件

     

    Quartz的简单使用:

    创建Job

    public class HelloQuartz implements Job {
            @Override
            public void execute(JobExecutionContext context) throws JobExecutionException {
                   //context 是任务执行的上下文,可以拿到任务的Jobkey(保存任务的name,group等信息)
                   String jobName = context.getJobDetail().getKey().getName();
                   System.out.println("任务key:" + jobName+"  Hello Quartz !");
            }
    
    }

    执行Job

    public class QuartzTest {
            public static void main(String[] args) throws InterruptedException {
    
                    //通过schedulerFactory获取一个调度器
                    SchedulerFactory schedulerfactory = new StdSchedulerFactory();
                    Scheduler scheduler = null;
                    try {
                            // 通过schedulerFactory获取一个调度器
                            scheduler = schedulerfactory.getScheduler();
              
                            //真正执行的任务并不是Job接口的实例,而是用反射的方式实例化的一个JobDetail实例    
                            // 创建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();
                    }
    
            }
    }
     
    入门步骤总结:
    1、自定义job 实现Job接口,重写execute方法
    2、通过SchedulerFactory获取一个调度器
    3、创建JobDetail,绑定自定义job类,设置组和名称
    4、创建Trigger,设置组和名称,及其它配置
    5、将Job和Trigger放入scheduler(或将Trigger绑定Job)
    6、启动和结束任务

     

    转载自:http://blog.csdn.net/wenniuwuren/article/details/41483667

  • 相关阅读:
    用场景来规划测试工作
    冲刺第二十天 到二十二天
    冲刺第十九天
    冲刺第十八天
    阅读《构建之法》第13-17章(包含读后感)
    冲刺第5,6天(5月25,26日)
    冲刺第四天(2天合一起当一篇随笔,明天会在这篇里继续更新)
    冲刺第二天
    作业5.2
    作业5.1
  • 原文地址:https://www.cnblogs.com/majay/p/6340176.html
Copyright © 2011-2022 走看看