Quartz原理

一、核心概念

Quartz的原理不是很复杂,只要搞明白几个概念,然后知道如何去启动和关闭一个调度程序即可。

1、Job
表示一个工作,要执行的具体内容。此接口中只有一个方法execute。我们的任务类实现该接口,重写execute方法即是要进行任务调度的方法。
void execute(JobExecutionContext context)

2、JobDetail
JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。

3、Trigger代表一个调度参数的配置,什么时候去调。

4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。正常情况下一个应用只需要一个Scheduler对象。

二、结构

1、scheduler是一个计划调度器容器,容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。

2、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业。

所有工作线程都会放在线程池中,即所有工作线程都放在SimpleThreadPool实例的一个LinkedList成员变量中。WorkerThread是SimpleThreadPool的内部类,这么设计可能是因为不想继承SimpleThreadPool但又想调用其protected方法,或者想隐藏WorkerThread。线程池还拥有两个LinkedList:availWorkers和busyWorkers,分别存放空闲和正在执行job的工作线程。

调度器线程以{instanceName}_QuartzSchedulerThread命名。该线程将根据trigger找出要待运行job,然后从threadpool中拿出工作线程来执行。调度器线程主体是QuartzSchedulerThread对象。

具体调用链是从StdScheduler -> QuartzScheduler -> QuartzSchedulerResources -> SimpleThreadPool(配置文件指定) -> WorkerThread

Quartz原理_第1张图片
image.png

3、将上述的结构用一个图来表示

Quartz原理_第2张图片
image.png

https://www.jianshu.com/p/bab8e4e32952

三、底层细节

1、集群之间的并发通过数据库行锁来控制,分为两步:获取TRIGGER及执行JOBDETAIL
quartz定时任务框架调度机制解析
2、调度器事实上也是采用轮询的方法,底层使用wait、notify来控制调度器的暂停等等
QuartzSchedulerThread.java

// 调度器线程一旦启动,将一直运行此方法
public void run() {
  ......
  // while()无限循环,每次循环取出时间将到的trigger,触发对应的job,直到调度器线程被关闭
  // halted是一个AtomicBoolean类变量,有个volatile int变量value,其get()方法仅仅简单的一句return value != 0,get()返回结果表示调度器线程是否开关
  // volatile修饰的变量,存取必须走内存,不能通过cpu缓存,这样一来get总能获得set的最新真实值,因此volatile变量适合用来存放简单的状态信息
  // 顾名思义,AtomicBoolean要解决原子性问题,但volatile并不能保证原子性,详见http://blog.csdn.net/wxwzy738/article/details/43238089
  while (!halted.get()) {
     try {
        // check if we're supposed to pause...
        // sigLock是个Object对象,被用于加锁同步
        // 需要用到wait(),必须加到synchronized块内
        synchronized (sigLock) {
            while (paused && !halted.get()) {
                try {
                    // wait until togglePause(false) is called...
                    // 这里会不断循环等待,直到QuartzScheduler.start()调用了togglePause(false)
                    // 调用wait(),调度器线程进入休眠状态,同时sigLock锁被释放
                    // togglePause(false)获得sigLock锁,将paused置为false,使调度器线程能够退出此循环,同时执行sigLock.notifyAll()唤醒调度器线程
                    sigLock.wait(1000L);
                } catch (InterruptedException ignore) {}
            }
            ......
        }
        ......
        // 如果线程池中的工作线程个数 > 0
        if(availThreadCount > 0) {
            ......
            // 获取马上到时间的trigger
            // 允许取出的trigger个数不能超过一个阀值,这个阀值是线程池个数与org.quartz.scheduler.batchTriggerAcquisitionMaxCount配置值间的最小者
            triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
            ......
            // 执行与trigger绑定的job
            // shell是JobRunShell对象,实现了Runnable接口
            // SimpleThreadPool.runInThread(Runnable)从线程池空闲列表中取出一个工作线程
            // 工作线程执行WorkerThread.run(Runnable),详见下方WorkerThread的讲解
            if (qsRsrcs.getThreadPool().runInThread(shell) == false) { ...... }
        } else {......}
        ......
    } catch(RuntimeException re) {......}
  } // while (!halted)
  ......
}

WorkerThread.java

public void run(Runnable newRunnable) {
        synchronized(lock) {
            if(runnable != null) {
                throw new IllegalStateException("Already running a Runnable!");
            }

            runnable = newRunnable;
            lock.notifyAll();
        }
}

// 工作线程一旦启动,将一直运行此方法
@Override
public void run() {
        boolean ran = false;
        
        // 工作线程一直循环等待job,直到线程被关闭,原理同QuartzSchedulerThread.run()中的halted.get()
        while (run.get()) {
            try {
               // 原理同QuartzSchedulerThread.run()中的synchronized (sigLock)
               // 锁住lock,不断循环等待job,当job要被执行时,WorkerThread.run(Runnable)被调用,job运行环境被赋值给runnable
                synchronized(lock) {
                    while (runnable == null && run.get()) {
                        lock.wait(500);
                    }
                    // 开始执行job
                    if (runnable != null) {
                        ran = true;
                        // runnable.run()将触发运行job实现类(比如JobImpl.execute())
                        runnable.run();
                    }
                }
            } catch (InterruptedException unblock) {
             ......
            }
        }
        ......
}

你可能感兴趣的:(Quartz原理)