Quartz源码分析(一)------ 以线程等待的方式实现按时间调度

Quartz是运用最广的任务调度框架,它最核心的组成部分是Scheduler、Trigger、JobDetail,然后给Scheduler配置个线程QuartzSchedulerThread,此线程在Scheduler初始化时启动,等待Scheduler start,然后从JobStore里拿到最近要触发的Trigger,以线程等待的方式等到trigger触发时间点,之后就是执行trigger所关联的JobDetail,最后清扫战场。Scheduler初始化、start和trigger执行的时序图如下所示:

其中,最核心的地方是QuartzSchedulerThread运行机制。下面解析一下它的run方法:

[java]  view plain copy
  1. public void run() {  
  2.         boolean lastAcquireFailed = false;  
  3.           
  4.         while (!halted) {  
  5.             try {  
  6.                 // check if we're supposed to pause...  
  7.                 synchronized (pauseLock) {  
  8.                     while (paused && !halted) {  
  9.                         try {  
  10.                             // wait until togglePause(false) is called...  
  11.                             pauseLock.wait(100L);  
  12.                         } catch (InterruptedException ignore) {  
  13.                         }  
  14.                     }  
  15.       
  16.                     if (halted) {  
  17.                         break;  
  18.                     }  
  19.                 }  
  20.            ......  
  21.       }  
  22. }  

 以上是run的最开头的一段,不难看出这是在等待scheduler的start,实际上Quartz就是通过线程的wait或sleep来实现时间调度。继续看代码:

[java]  view plain copy
  1. Trigger trigger = null;  
  2. long now = System.currentTimeMillis();  
  3. signaled = false;  
  4. try {  
  5.     trigger = qsRsrcs.getJobStore().acquireNextTrigger(  
  6.             ctxt, now + idleWaitTime);  
  7.     lastAcquireFailed = false;  
  8. catch (JobPersistenceException jpe) {  
  9.     if(!lastAcquireFailed) {  
  10.         qs.notifySchedulerListenersError(  
  11.             "An error occured while scanning for the next trigger to fire.",  
  12.             jpe);  
  13.     }  
  14.     lastAcquireFailed = true;  
  15. catch (RuntimeException e) {  
  16.     if(!lastAcquireFailed) {  
  17.         getLog().error("quartzSchedulerThreadLoop: RuntimeException "  
  18.                 +e.getMessage(), e);  
  19.     }  
  20.     lastAcquireFailed = true;  
  21. }  

这段代码是从jobStore里拿到下一个要执行的trigger,一般情况下jobStore使用的是RAMJobStore,即trigger等相关信息存放在内存里,如果需要把任务持久化就得使用可持久化JobStore。继续看代码:

 

[java]  view plain copy
  1. now = System.currentTimeMillis();  
  2. long triggerTime = trigger.getNextFireTime().getTime();  
  3. long timeUntilTrigger = triggerTime - now;  
  4. long spinInterval = 10;  
  5. int numPauses = (int) (timeUntilTrigger / spinInterval);  
  6. while (numPauses >= 0 && !signaled) {  
  7.     try {  
  8.         Thread.sleep(spinInterval);  
  9.     } catch (InterruptedException ignore) {  
  10.     }  
  11.     now = System.currentTimeMillis();  
  12.     timeUntilTrigger = triggerTime - now;  
  13.     numPauses = (int) (timeUntilTrigger / spinInterval);  
  14. }  
  15. if (signaled) {  
  16.     try {  
  17.         qsRsrcs.getJobStore().releaseAcquiredTrigger(  
  18.                 ctxt, trigger);  
  19.     } catch (JobPersistenceException jpe) {  
  20.         qs.notifySchedulerListenersError(  
  21.                 "An error occured while releasing trigger '"  
  22.                         + trigger.getFullName() + "'",  
  23.                 jpe);  
  24.         // db connection must have failed... keep  
  25.         // retrying until it's up...  
  26.         releaseTriggerRetryLoop(trigger);  
  27.     } catch (RuntimeException e) {  
  28.         getLog().error(  
  29.             "releaseTriggerRetryLoop: RuntimeException "  
  30.             +e.getMessage(), e);  
  31.         // db connection must have failed... keep  
  32.         // retrying until it's up...  
  33.         releaseTriggerRetryLoop(trigger);  
  34.     }  
  35.     signaled = false;  
  36.     continue;  
  37. }  

此段代码是计算下一个trigger的执行时间和现在系统时间的差,然后通过循环线程sleep的方式暂停住此线程,一直等到trigger的执行时间点。继续看代码:

[java]  view plain copy
  1. import org.quartz.core.JobRunShell;  
  2. JobRunShell shell = null;  
  3. try {  
  4.     shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell();  
  5.     shell.initialize(qs, bndle);  
  6. catch (SchedulerException se) {  
  7.     try {  
  8.         qsRsrcs.getJobStore().triggeredJobComplete(ctxt,  
  9.                 trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);  
  10.     } catch (SchedulerException se2) {  
  11.         qs.notifySchedulerListenersError(  
  12.                 "An error occured while placing job's triggers in error state '"  
  13.                         + trigger.getFullName() + "'", se2);  
  14.         // db connection must have failed... keep retrying  
  15.         // until it's up...  
  16.         errorTriggerRetryLoop(bndle);  
  17.     }  
  18.     continue;  
  19. }  
  20. if (qsRsrcs.getThreadPool().runInThread(shell) == false) {  
  21.     try {  
  22.         getLog().error("ThreadPool.runInThread() return false!");  
  23.         qsRsrcs.getJobStore().triggeredJobComplete(ctxt,  
  24.                 trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);  
  25.     } catch (SchedulerException se2) {  
  26.         qs.notifySchedulerListenersError(  
  27.                 "An error occured while placing job's triggers in error state '"  
  28.                         + trigger.getFullName() + "'", se2);  
  29.         // db connection must have failed... keep retrying  
  30.         // until it's up...  
  31.         releaseTriggerRetryLoop(trigger);  
  32.     }  
  33. }  

此段代码就是包装trigger,然后通过以JobRunShell为载体,在threadpool里执行trigger所关联的jobDetail。

之后的代码就是清扫战场,就不在累述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值