quartz 2.2.x 源码学习 基本执行流程分析
来源:互联网 发布:老蛙镜头 知乎 编辑:程序博客网 时间:2024/05/07 08:01
quartz 官网中给出了一些基本概念,请先阅读官网相关概念。
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/
下面对最简单的一个任务调度工作进行分析,下面的代码每隔三秒中不断重复执行任务SimpleJob。
public class JobStart { public static void main(String[] args) throws SchedulerException { SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); sched.clear(); String instanceId = sched.getSchedulerInstanceId(); JobDetail detail = JobBuilder.newJob(SimpleJob.class) .withIdentity("Ins Id " + instanceId, instanceId) .requestRecovery() .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("Ins Id " + instanceId,instanceId) .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build(); sched.scheduleJob(detail, trigger); sched.start(); }}@DisallowConcurrentExecutionpublic class SimpleJob implements Job{ private static Logger _log = LoggerFactory.getLogger(SimpleJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { if(context.isRecovering()){ _log.info("is recovering"); }else{ _log.info("not recovering"); } try { _log.info("do time consumed job start"); Thread.sleep(5000); _log.info("do time consumed job end"); } catch (InterruptedException e) { e.printStackTrace(); } }}
quartz 执行任务可以总结为创建JobDetail描述待执行任务,创建Trigger描述什么时候去触发任务。最后将JobDetail与Trigger注册在调度器中,调度器启动后遍开始对任务调度。
因此了解其基本过程需要明白 创建调度器完成哪些工作?调度工作什么时候开始?任务如何被执行?
示例代码中,首先获取一个调度器。
Scheduler sched = sf.getScheduler();
进入该方法内部
org.quartz.impl.StdSchedulerFactory.getScheduler() if (cfg == null) { initialize(); } SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(getSchedulerName()); if (sched != null) { if (sched.isShutdown()) { schedRep.remove(getSchedulerName()); } else { return sched; } } sched = instantiate(); return sched;
获取调度器对象时,首先查找缓存,如果有就直接拿,若不存在则调用instantiate()方法来创建调度器对象。
进入instantiate方法内部,定义了许多变量。
org.quartz.impl.StdSchedulerFactory.instantiate() private Scheduler instantiate() throws SchedulerException { if (cfg == null) { initialize(); } if (initException != null) { throw initException; } JobStore js = null; ThreadPool tp = null; QuartzScheduler qs = null; DBConnectionManager dbMgr = null; String instanceIdGeneratorClass = null; Properties tProps = null; ......
ThreadPool tp为线程池对象,在该方法内部可以看到该线程池的创建 是通过从属性文件quartz.properties中拿到org.quartz.threadPool所定义的线城池类来创建。本文使用的是 org.quartz.simpl.SimpleThreadPool。而最终任务通过线程池来管理。
public static final String PROP_THREAD_POOL_PREFIX = "org.quartz.threadPool"; String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName()); if (tpClass == null) { initException = new SchedulerException( "ThreadPool class not specified. "); throw initException; } try { tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance(); } catch (Exception e) { initException = new SchedulerException("ThreadPool class '" + tpClass + "' could not be instantiated.", e); throw initException; }
后续代码中会创建QuartzScheduler对象,最终来启动调动线程。
qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry);
在该构造器内部,会创建QuartzSchedulerThread对象,而QuartzSchedulerThread负责对我们定义的Job进行调度。当创建完后
提交给执行器对象schedThreadExecutor.execute(this.schedThread);准备执行。
public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval) throws SchedulerException { this.resources = resources; if (resources.getJobStore() instanceof JobListener) { addInternalJobListener((JobListener)resources.getJobStore()); } this.schedThread = new QuartzSchedulerThread(this, resources); ThreadExecutor schedThreadExecutor = resources.getThreadExecutor(); schedThreadExecutor.execute(this.schedThread); if (idleWaitTime > 0) { this.schedThread.setIdleWaitTime(idleWaitTime); }
当sf.getScheduler();执行完毕后,线程池,执行器,调度器,负责调度Job的线程都已创建完毕。而任务调度线程QuartzSchedulerThread也准备执行。
可以看到sf.getScheduler();调用完毕后,任务调度线程处于暂停状态,并不断检查状态等待恢复并执行任务。
org.quartz.core.QuartzSchedulerThread.run() while (!halted.get()) { try { // check if we're supposed to pause... synchronized (sigLock) { while (paused && !halted.get()) { try { // wait until togglePause(false) is called... sigLock.wait(1000L); } catch (InterruptedException ignore) { } } if (halted.get()) { break; } }
在 QuartzSchedulerThread 构造方法中会设置两个变量初始值。
paused = true; halted = new AtomicBoolean(false);
当我们获取到调度器对象,并指定trigger与detail后,调用start方法。
当调用执行器的 org.quartz.core.QuartzScheduler.start()方法在该方法内部会调用方法 schedThread.togglePause(false);将paused变量设置为false,并唤醒QuartzSchedulerThread线程。随QuartzSchedulerThread线程开始执行调度任务。
org.quartz.core.QuartzScheduler.start() void togglePause(boolean pause) { synchronized (sigLock) { paused = pause; if (paused) { signalSchedulingChange(0); } else { sigLock.notifyAll(); } }
至此任务调度工作准备开始,Job等待被调用。
QuartzSchedulerThread的run方法中检测到变量状态被修改,开始调度工作。
public void run() { boolean lastAcquireFailed = false; while (!halted.get()) { try { // check if we're supposed to pause... synchronized (sigLock) { while (paused && !halted.get()) { try { // wait until togglePause(false) is called... sigLock.wait(1000L); } catch (InterruptedException ignore) { } } if (halted.get()) { break; } } int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads(); if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads... List<OperableTrigger> triggers = null; long now = System.currentTimeMillis(); clearSignaledSchedulingChange();... 省略
run方法中的执行过程可粗略归纳为
1.查找将要触发的Trigger
2.检测条件,等待执行
3.执行任务。
4.释放Trigger
在执行时,任务被包装为JobRunShell来运行。
当qsRsrcs.getThreadPool().runInThread(shell)被调用时,我们自己的Job被提交,并等待线程池调度执行。
JobRunShell shell = null; JobRunShell shell = null; try { shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle); shell.initialize(qs); } catch (SchedulerException se) { qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR); continue; } if (qsRsrcs.getThreadPool().runInThread(shell) == false) { // this case should never happen, as it is indicative of the // scheduler being shutdown or a bug in the thread pool or // a thread pool being used concurrently - which the docs // say not to do... getLog().error("ThreadPool.runInThread() return false!"); qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR); }
在 qsRsrcs.getThreadPool().runInThread(shell)方法中会创建一个线程来运行我们定义的Job,而Job的信息则是通过JobDetail来获取。经过上述过程就形成了quartz 进行任务调度的基本过程。
最后总结,启动时的三个主要方法,及工作概述如下。
Scheduler sched = sf.getScheduler();
完成了线程池创建,调度线程创建,调度线程初始处理暂停状态。
sched.scheduleJob(detail, trigger);
指定JobDetail与Trigger。
sched.start();
修改状态未,使调度线程开始运行。任务以JobRunShell的形式被执行。
- quartz 2.2.x 源码学习 基本执行流程分析
- MyBatis源码学习笔记(十)SQL执行流程分析
- redis执行流程源码分析
- struts2执行流程源码分析
- quartz-2.2.3源码分析
- 1. x-loader执行流程分析
- angularjs源码分析之:angularjs执行流程
- Mybatis 源码 sql执行流程分析
- [Android] Retrofit 执行流程源码分析
- Spring MVC 执行流程和源码分析
- tcc-transaction 执行流程源码分析
- angularjs源码分析之:angularjs执行流程
- springmvc----源码分析之springmvc执行流程
- struts2执行流程深入探索-源码分析
- SpringMVC DispatcherServlet执行流程及源码分析
- cocos2D-X源码分析之从cocos2D-X学习OpenGL(5)---绘制基本图形
- cocos2D-X源码分析之从cocos2D-X学习OpenGL(16)----基本光照
- quartz源码分析——执行引擎和线程模型
- (转)打造你的开发神器——介绍Android Studio上的几个插件
- DecimalFormat 用法
- Linux怎样发展壮大
- Leetcode-530. Minimum Absolute Difference in BST
- Vuex的五个核心属性
- quartz 2.2.x 源码学习 基本执行流程分析
- java安全架构____RSA加密原理(1)
- 无损压缩
- Oracle03
- req.flash的用法
- 使用免费的代码管理仓库Bitbuket
- 手机/移动前端开发需要注意的20个要点
- 81. Search in Rotated Sorted Array II**
- 接深度学习,opencv人脸识别,目标检测等项目