Quartz 实现机制

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.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
org.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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Quartz是一个强大的作业调度框架,可以用来实现定时任务。使用Quartz中的反射机制可以方便地调用应用程序中的方法。 要使用Quartz的反射机制调用应用程序中的方法,需要执行以下步骤: 1. 创建一个实现了Job接口的类,该类中需要实现execute方法,该方法中实现需要执行的业务逻辑。 2. 在该类中定义一个名为"methodName"的字符串变量,用于指定要调用的方法名。 3. 在execute方法中使用反射机制调用应用程序中的方法。具体实现方法如下: ``` Class<?> clazz = Class.forName("应用程序中的类名"); Object obj = clazz.newInstance(); Method method = clazz.getMethod(methodName, 参数类型); method.invoke(obj, 参数值); ``` 其中,"应用程序中的类名"是指需要调用的类的全路径名,"methodName"是指需要调用的方法名,"参数类型"和"参数值"分别指定需要传递给该方法的参数类型和参数值。 4. 在Quartz中配置JobDetail和Trigger,将实现了Job接口的类和要调用的方法名传递给JobDataMap中,然后在execute方法中获取JobDataMap中的方法名,并使用反射机制调用应用程序中的方法。 ``` JobDetail jobDetail = JobBuilder.newJob(实现了Job接口的类.class).build(); jobDetail.getJobDataMap().put("methodName", "要调用的方法名"); Trigger trigger = TriggerBuilder.newTrigger().build(); scheduler.scheduleJob(jobDetail, trigger); ``` 在execute方法中获取JobDataMap中的方法名,并使用反射机制调用应用程序中的方法: ``` String methodName = jobExecutionContext.getJobDetail().getJobDataMap().getString("methodName"); Class<?> clazz = Class.forName("应用程序中的类名"); Object obj = clazz.newInstance(); Method method = clazz.getMethod(methodName, 参数类型); method.invoke(obj, 参数值); ``` 通过以上步骤,就可以使用Quartz的反射机制调用应用程序中的方法了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值