Quartz 实现机制

来源:互联网 发布:iphone录屏软件 编辑:程序博客网 时间:2024/06/04 19:00

quartz 是目前使用较广的调度管理工具,提供了与Linux下Cron类似的功能,实际上还更强大,可以方便的嵌入到Java系统中,最近就这个调度工具进行了一些简单的研究,主要研究调度后台的机制,quartz中包括的分布式调度和基于数据库的记录不在此处的研究范围,记录在这里方便以后回顾

quartz 调度的主要结构

  1.  quartz调度主要由三块组成:Scheduler 调度服务,Trigger 触发器,Job任务,各个主要的分工大致如下
  •       Scheduler 调度框架的入口,可调度多个触发器和任务
  • Trigger 触发器,由多个版本支持简单调度和复杂的cron调度表达式
  • Job 具体的工作组件,业务逻辑主要在此实现即可
注意,Trigger和Job是成对出现,作为一个整体注册到Scheduler中

调度流程

       Scheduler首先通过工厂类进行初始化,主要的初始化工厂为StdSchedulerFactory,初始化时会在系统路径下搜索 quartz.properties配置文件,该配置文件只要放在Java的classPath可搜索到的位置即可,可灵活配置执行线程,数据库等等
注意,如果有冲突的话,此处的配置会覆盖Jdk自身的一些配置参数,这里的配置参数有点意思,参数大致由三部分组成,举例如下
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount: 10org.quartz.threadPool.threadPriority: 5org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
这里前三个为参数组的名称,第四个为参数属性,冒号后面是参数值
初始化时主要初始化QuartzSchedulerResources对象,该对象中包括了调度的相关信息,如执行线程池(负责执行具体的任务),线程执行器(负责解析调度表达式,产生执行任务给执行线程池)等
初始化时会创建一个QuartzScheduler对象,该对象创建过程中后会创建一个QuartzSchedulerThread对象,这个线程在上一步的线程执行器中开始准备遍历QuartzSchedulerResources中的触发器对象列表
当调用Scheduler的start方法后,执行线程的执行标志打开,执行线程开始遍历触发器列表,当然,如果触发器列表中无数据,或者不再调度时间范围内,执行线程会休眠,而不是一直不停的遍历,代码如下
 long now = System.currentTimeMillis();                long waitTime = now + getRandomizedIdleWaitTime();                long timeUntilContinue = waitTime - now;                synchronized(sigLock) {                    try {                      if(!halted.get()) {                        // QTZ-336 A job might have been completed in the mean time and we might have                        // missed the scheduled changed signal by not waiting for the notify() yet                        // Check that before waiting for too long in case this very job needs to be                        // scheduled very soon                        if (!isScheduleChanged()) {                          sigLock.wait(timeUntilContinue);                        }                      }                    } catch (InterruptedException ignore) {                    }                }


当注册一个新的(trigger,job)信息后,执行线程通过QuartzSchedulerResources的acquireNextTriggers方法获取下一个执行时间片内将要执行的触发器信息列表(默认时间片为30s),获取后按顺序对执行对象进行封装,再调用执行线程池进行执行任务
调度需要注意的地方
      触发器和任务一旦注册到调度服务站中,每次调度时都是clone的触发器和任务对象,避免对原来的任务对象产生影响,在更新时会同时更新原对象和clone后的对象
     
QuartzSchedulerResources 中触发器的保存是一个TreeSet,不用想是按照调度时间进行排序的,即下次执行时间靠前的触发器会排在最前面,以此类推,遍历时,拿到最早的触发器,然后删除,再判断是否在时间片范围内,如果在,则直接加入到待执行List中,由执行线程执行(同时会更新下次执行时间),执行线程执行完毕后,再将该调度器加入到TreeSet中(同时会更新下次执行时间)
Quartz对Cron的实现原理
     这点非常有意思,cron表达式的解析及执行采用了非常巧妙的办法,速度非常快。思路如下,一个corn表达式的七位,分别存储在7个TreeSet中,每个位按从小到大,保存所有该位可能的枚举值(注意,相互之间没有关联哦,天-月/天-周 这两个二选一的当然是不能共存的),这一点和常规的思维有电不同,常规对表达式的判断可能是关联性的
    当判断一个时间值是否符合cron表达式时,是分别取出待比较时间的,秒,分,时,天,月,周,年,和个组合值进行比较,如果满足条件则返回true,否则返回false
    当用在调度平台提供长期服务时,调度依赖前一个任务执行时间,判断下一次执行时间也是非常简单,即按 秒,分,时,天,月,周,年,找到比待比较时间大的值即可 ,举例说明
Corn表达式如果是 30/3 5/4  * 3 3 ? *  即 3月3号0点开始,每个小时的5,9,13.....分,每分的第30,33,36秒开始执行
假设上次执行时间为 3月3号8点05分59秒,下次执行时间的判断为
秒 大于57的为下一秒的30(此处进位1)
分 由于秒位进1,此处取9
小时 由于没有进位,仍然是 8
天   由于没有进位仍然是3
......
最后产生的下一次执行时间为 3月3号8点09分30秒
quartz中比较精华的地方就在这里了吧,CronExpression类大家可以看看
0 0
原创粉丝点击