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

Quartz作为广泛使用的工作调度框架,其核心组件包括Scheduler、Trigger和JobDetail。文章深入解析了QuartzSchedulerThread的工作原理,包括如何获取下一个待执行的Trigger,并通过线程等待或睡眠方式精确调度。

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

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

public void run() { boolean lastAcquireFailed = false; while (!halted) { try { // check if we're supposed to pause... synchronized (pauseLock) { while (paused && !halted) { try { // wait until togglePause(false) is called... pauseLock.wait(100L); } catch (InterruptedException ignore) { } } if (halted) { break; } } ...... } }

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

Trigger trigger = null; long now = System.currentTimeMillis(); signaled = false; try { trigger = qsRsrcs.getJobStore().acquireNextTrigger( ctxt, now + idleWaitTime); lastAcquireFailed = false; } catch (JobPersistenceException jpe) { if(!lastAcquireFailed) { qs.notifySchedulerListenersError( "An error occured while scanning for the next trigger to fire.", jpe); } lastAcquireFailed = true; } catch (RuntimeException e) { if(!lastAcquireFailed) { getLog().error("quartzSchedulerThreadLoop: RuntimeException " +e.getMessage(), e); } lastAcquireFailed = true; }

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

now = System.currentTimeMillis(); long triggerTime = trigger.getNextFireTime().getTime(); long timeUntilTrigger = triggerTime - now; long spinInterval = 10; int numPauses = (int) (timeUntilTrigger / spinInterval); while (numPauses >= 0 && !signaled) { try { Thread.sleep(spinInterval); } catch (InterruptedException ignore) { } now = System.currentTimeMillis(); timeUntilTrigger = triggerTime - now; numPauses = (int) (timeUntilTrigger / spinInterval); } if (signaled) { try { qsRsrcs.getJobStore().releaseAcquiredTrigger( ctxt, trigger); } catch (JobPersistenceException jpe) { qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + trigger.getFullName() + "'", jpe); // db connection must have failed... keep // retrying until it's up... releaseTriggerRetryLoop(trigger); } catch (RuntimeException e) { getLog().error( "releaseTriggerRetryLoop: RuntimeException " +e.getMessage(), e); // db connection must have failed... keep // retrying until it's up... releaseTriggerRetryLoop(trigger); } signaled = false; continue; }

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

import org.quartz.core.JobRunShell; JobRunShell shell = null; try { shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell(); shell.initialize(qs, bndle); } catch (SchedulerException se) { try { qsRsrcs.getJobStore().triggeredJobComplete(ctxt, trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR); } catch (SchedulerException se2) { qs.notifySchedulerListenersError( "An error occured while placing job's triggers in error state '" + trigger.getFullName() + "'", se2); // db connection must have failed... keep retrying // until it's up... errorTriggerRetryLoop(bndle); } continue; } if (qsRsrcs.getThreadPool().runInThread(shell) == false) { try { getLog().error("ThreadPool.runInThread() return false!"); qsRsrcs.getJobStore().triggeredJobComplete(ctxt, trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR); } catch (SchedulerException se2) { qs.notifySchedulerListenersError( "An error occured while placing job's triggers in error state '" + trigger.getFullName() + "'", se2); // db connection must have failed... keep retrying // until it's up... releaseTriggerRetryLoop(trigger); } }

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值