Quartz 快速开始

一、Quartz简介

深入解读Quartz任务调度器
参考URL: https://blog.youkuaiyun.com/DuShiWoDeCuo/article/details/78543110

Quartz大致可分为三个主要的核心:
1、调度器Scheduler:是一个计划调度器容器,容器里面可以盛放众多的JobDetail和Trigger,当容器启动后,里面的每个JobDetail都会根据Trigger按部就班自动去执行.
2、任务Job:要执行的具体内容。JobDetail:具体的可执行的调度程序,包含了这个任务调度的方案和策略。
3、触发器Trigger:调度参数的配置,什么时候去执行调度。

原理:
可以这么理解它的原理:调度器就相当于一个容器,装载着任务和触发器。任务和触发器又是绑定在一起的,然而一个任务可以对应多个触发器,但一个触发器却只能对应一个任务。
当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的任务作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。

二、重要组成

Job

Job:表示一个工作,要执行的具体内容。此接口中只有一个方法。要创建一个任务,必须得实现这个接口。该接口只有一个execute方法,任务每次被调用的时候都会执行这个execute方法的逻辑,类似TimerTask的run方法,在里面编写业务逻辑。

生命周期:在每次调度器执行job时,它在调用execute方法前会创建一个新的job实例,当调用完成之后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。

  • JobBuilder:可向任务传递数据,通常情况下,我们使用它就可向任务类发送数据了,如有特别复杂的传递参数,它提供了一个传递递:JobDataMap对象的方法
JobDetail jobDetail =  JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").build();
  • JobDetail:用来保存我们任务的详细信息。一个JobDetail可以有多个Trigger,但是一个Trigger只能对应一个JobDetail。
  • JobStore:负责跟踪所有你给scheduler的“工作数据”:jobs, triggers, calendars, 等。
    RAMJobStore:是使用最简单的也是最高效(依据CPU时间)的JobStore 。RAMJobStore 正如它名字描述的一样,它保存数据在RAM。缺点是你的应用结束之后所有的数据也丢失了–这意味着RAMJobStore 不具有保持job和trigger持久的能力。对于一些程序是可以接受的,甚至是期望的,但对于其他的程序可能是灾难性的。使用RAMJobStore配置Quartz:配置如下
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 

JDBCJobStore: 以JDBC的方式保存数据在数据库中。它比RAMJobStore的配置复杂一点,也没有RAMJobStore快。几乎适用于所有的数据库,广泛用于 Oracle。PostgreSQL, MySQL, MS SQLServer, HSQLDB, 和DB2。使用JDBCJobStore之前你必须首先创建一系列Quartz要使用的表。你可以发现表创建语句在Quartz发布目录的 “docs/dbTables”下面。你需要确定你的应用要使用的事务类型。如果你不想绑定调度命令(例如增加和移除Trigger)到其他的事务,你可以使用JobStoreTX (最常用的选择)作为你的Jobstore。如果你需要Quartz和其他的事务(例如在J2EE应用服务器中)一起工作,你应该使用JobStoreCMT ,Quartz 将让应用服务器容器管理这个事务。使用JobStoreTx配置Quartz:

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate #配置表的前缀 org.quartz.jobStore.tablePrefix = QRTZ_ #使用JNDI数据源的时候,数据源的名字 org.quartz.jobStore.dataSource = myDS 

TerracottaJobStore:提供了一个方法:在不使用数据库的情况下使它具有收缩性和强壮性。可以是集群的也可以是非集群的,在这两种情况下为你的job数据提供了一个存储机制用于应用程序重启之间持久,因为数据是存储在Terracotta服务器。它的性能比使用数据库访问JDBCJobStore好一点儿(大约是一个数量级),但是明显比RAMJobStore慢。使用TerracottaJobStore配置Quartz:

org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore  
org.quartz.jobStore.tcConfigUrl = localhost:9510  

JobDataMap:中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

JobDetail jobDetail =  JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").usingJobData("date1","存内容").build(); 
public class TestJob implements Job { /**把要执行的操作,写在execute方法中 */ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobKey key = jobExecutionContext.getJobDetail().getKey(); JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); String date1 = jobDataMap.getString("date1"); } }

触发器:用来触发执行Job

触发器通用属性:
Jobkey:表示job实例的标识,触发器被触发时,该指定的job实例会被执行
StartTime:表示触发器的时间表首次被触发的时间,它的值类型为:java.util.Date
EndTime:指定触发器的不再被触发的时间,它的值类型为:java.util.Date

未正常触发的任务:misfire job

没有在正常触发时间点触发的任务。主要由一下几种情况导致:

触发时间在应用不可用的时间内,比如重启
上次的执行时间过长,超过了下次触发的时间
任务被暂停一段时间后,重新被调度的时间在下次触发时间之后

处理misfire job的策略,需要在创建trigger的时候配置,每种trigger对应的枚举值都不同,具体在接口里面有定义。CronTrigger有2种处理misfire的策略:
在这里插入图片描述

线程

负责任务调度的几个线程:
(1)任务执行线程:通常使用一个线程池(SimpleThreadPool)维护一组线程,负责实际每个job的执行。
(2)Scheduler调度线程QuartzSchedulerThread :轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务。
(3)处理misfire job的线程MisfireHandler:轮训所有misfire的trigger,原理就是从数据库中查询所有下次触发时间小于当前时间的trigger,按照每个trigger设定的misfire策略处理这些trigger。

三、纯spring环境下-quartz基本使用

1. maven引入

        <!--quartz-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

2. spring 配置bean SchedulerFactoryBean

使用SchedulerFactoryBean集成Quarz Job与Spring
参考URL: https://www.iteye.com/blog/angelbill3-2371787

Spring通过SchedulerFactoryBean实现调度任务的配置
使用SchedulerFactoryBean集成Quarz Job与Spring

本次集成主要用到了Spring提供的
org.springframework.scheduling.quartz.SchedulerFactoryBean,该类使得Spring application context可以创建或管理Quartz的Scheduler,包括注册JobDetails、Calendars和Triggers等。
有了这个类,可以Retire掉org.quartz.ee.servlet.QuartzInitializerListener这个Listener。

注:这个类兼容Quartz 2.1.4及以上版本,Spring 4.1及以上版本。

spring xml配置:

    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.threadCount">50</prop>
            </props>
        </property>
    </bean>

spring 注解配置(未验证测试):

import java.util.Properties;  
import org.springframework.context.annotation.Bean;  
import org.springframework.scheduling.quartz.SchedulerFactoryBean;  
import org.springframework.stereotype.Component;  
@Component  
public class ComponentFactory {  
  
    @Bean  
    public SchedulerFactoryBean getSchedulerFactoryBean(JobFactory myJobFactory) throws Exception {  
        SchedulerFactoryBean bean = new SchedulerFactoryBean();  
        bean.setJobFactory(myJobFactory);  
        bean.setSchedulerName("myscheduler");  
        Properties quartzProperties = new Properties();  
        quartzProperties.load(this.getClass().getResourceAsStream("/quartz.properties"));  
        bean.setQuartzProperties(quartzProperties);  
        return bean;  
    }     
}  

3. 定义一个Job实现类实现Job接口

具体执行job,是执行这个类的execute方法。

public class QuartzJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        LoggerUtil.info("quartz job starts");
        //TODO
        LoggerUtil.info("quartz job end");
    }
}

4. 定义一个Quartz定时任务管理类

接口如下:定义添加job、删除job、修改job、查询job等接口

/**
 * Quartz定时任务管理接口
 */
public interface QuartzManagerService {

    /**
     * 添加定时任务
     * @param jobName
     * @param cronStr
     */
    void addJob(String jobName, String cronStr);

    /**
     * 移除定时任务
     *
     * @param jobNames
     */
    void removeJob(List<String> jobNames);

    /**
     * 修改quartzJob表达式
     * @param name
     * @param cronStr
     * @return
     * @throws SchedulerException
     */
    boolean modifyQuartzJob(String name, String cronStr) throws SchedulerException;

    /**
     * 根据名字判断某个QuartzJob是否存在
     *
     * @param name
     * @return
     * @throws SchedulerException
     */
    boolean isExistQuartzJob(String name) throws SchedulerException;

}

注入之前spring 配置的bean SchedulerFactoryBean,实现相关 功能。
里面有具体使用,可以直接参考使用。

import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Date;
import java.util.List;

public class QuartzManagerServiceImpl implements QuartzManagerService{

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @Override
    public void addJob(String jobName, String cronStr) {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        //任务构建withIdentity:相当于给任务起了个名字
        JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                .withIdentity(new JobKey(jobName)).build();
        //表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronStr)
                .withMisfireHandlingInstructionDoNothing();
        //按新的cronExpression表达式构建一个新的trigger:
        // withIdentity:相当于给trigger起了个名字
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(scheduleBuilder)
                .withIdentity(new TriggerKey(jobName)).build();
        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            LoggerUtil.error("failed add job to scheduler: ", e);
        }
    }

    @Override
    public void removeJob(List<String> jobNames) {

        if(CollectionUtils.isEmpty(jobNames)){
            throw new ServiceException("jobnames empty");
        }

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        for (String name : jobNames) {
            try {
                //停止触发器
                scheduler.pauseTrigger(new TriggerKey(name));
                //移除触发器
                scheduler.unscheduleJob(new TriggerKey(name));
                //删除任务
                scheduler.deleteJob(new JobKey(name));
            } catch (SchedulerException e) {
                LoggerUtil.error("SchedulerException: ", e);
            }
        }

    }

    @Override
    public boolean modifyQuartzJob(String name, String cronStr) throws SchedulerException {
        LoggerUtil.info("modifyQuartzJob start !");

        if (!CronSequenceGenerator.isValidExpression(cronStr)) {
            LoggerUtil.info("modifyQuartzJob fail:{}", cronStr);
            return false;
        }

        Date date = null;
        TriggerKey triggerKey = new TriggerKey(name);
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        String oldTime = cronTrigger.getCronExpression();
        if (!oldTime.equalsIgnoreCase(cronStr)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronStr)
                    .withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name)
                    .withSchedule(cronScheduleBuilder).build();
            date = scheduler.rescheduleJob(triggerKey, trigger);
        }

        LoggerUtil.info("modifyQuartzJob end !");
        return date != null;

    }

    @Override
    public boolean isExistQuartzJob(String jobName) throws SchedulerException {
        LoggerUtil.info("isExistQuartzJob start !");

        TriggerKey triggerKey = new TriggerKey(jobName);
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        if (cronTrigger == null) {
            return false;
        }

        LoggerUtil.info("isExistQuartzJob end !");
        return true;
    }
}

总结:在上面的demo中,我们看到添加一个job 实现思路大致为:
注入schedulerFactoryBean
调 schedulerFactoryBean.getScheduler(); 获取Scheduler实例
创建JobDetail、CronTrigger实例
通过Scheduler实例scheduler.scheduleJob(jobDetail, trigger);

spring和quartz 动态增加删除任务

扩展:spring3整合quartz2,实现动态添加、删除定时任务
参考URL: https://blog.youkuaiyun.com/qq_35153200/article/details/79485238
详解Spring整合Quartz实现动态定时任务
参考URL: https://www.jb51.net/article/107339.htm

思路> 把任务与cronExpression存放在数据库中,最大化减少xml配置,创建一个工厂类,在实际调用时把任务的相关信息通过参数方式传入,由该工厂类根据任务信息来具体执行需要的操作,从而方便我们的动态修改。

四、非spring环境下:quartz基本使用

Quartz的基本使用之入门(2.3.0版本)
参考URL: https://www.cnblogs.com/zhanghaoliang/p/7886110.html

  1. StdSchedulerFactory创建schedule调度器实例

  2. 编写任务执行类implements Job复写execute

  3. 生成jobDetail和trigger实例作为参数传入schedule调度器

  4. schedule调度器启动即可

    1、创建调度工厂();    //工厂模式
    2、根据工厂取得调度器实例();   //工厂模式
    3、Builder模式构建子组件<Job,Trigger>    // builder模式, 如JobBuilder、TriggerBuilder、DateBuilder
    4、通过调度器组装子组件 调度器.组装<子组件1,子组件2…>   //工厂模式
    5、调度器.start();   //工厂模式

demo如下;

  1. 创建一个类 HelloJob.java,这个类是编写我们的具体要实现任务(打印Hello Quartz)
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;
public class HelloJob implements Job{
  public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //打印当前的执行时间 例如 2017-11-23 00:00:00
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("现在的时间是:"+ sf.format(date));
        //具体的业务逻辑
        System.out.println("Hello Quartz");
    }
}
  1. 创建一个类HelloScheduler.java,这个是具体触发我们的任务
public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        //创建一个jobDetail的实例,将该实例与HelloJob Class绑定
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("myJob").build();
        //创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
        SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
        //创建schedule实例
        StdSchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();
        scheduler.start();
        scheduler.scheduleJob(jobDetail,trigger);

    }
}

五、cron表达式

spring cron表达式及解析过程
参考URL: https://blog.youkuaiyun.com/ukulelepku/article/details/54310035

六、问题点整理

cron表达式后台格式校验问题

CronsequenceGenerator负责解析cron表达式并提供next接口。
Spring提供了CronSequenceGenerator类以便计算cron表达式的具体时间点的next方法(Spring3.2版本)和cron是否合法的 isValidExpression 方法(Spring4.3版本)。

使用CronsequenceGenerator的静态isValidExpression 方法即可。

计算cron表达式下次执行日期

spring其实已经提供了现成的类给大家使用,它就是CronSequenceGenerator:在spring-context包里

修改cron表达式内容立即生效思路

思路1: quartz调度器先删除这个任务再添加这个任务。
思路2: 代码解决 通过scheduler调度器对象的rescheduleJob方法

scheduler.rescheduleJob(triggerKey, trigger);
    public boolean modifyQuartzJob(String name, String cronStr) throws SchedulerException {
        Date date = null;
        TriggerKey triggerKey = new TriggerKey(name);
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldTime = cronTrigger.getCronExpression();
        if (!oldTime.equalsIgnoreCase(cronStr)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronStr);
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name)
                    .withSchedule(cronScheduleBuilder).build();
            date = scheduler.rescheduleJob(triggerKey, trigger);
        }
        return date != null;
    }

常见job不执行问题分析

quartz里job不执行的解决方案
参考URL: https://blog.youkuaiyun.com/dotalee/article/details/70845254

  1. quartz中线程总数太少导致
    生产环境中碰到会有job一直不执行的情况,后来分析是因为quartz中线程总数太少,而项目中所有的job都是并发执行的就会导致当到达时间节点时如果线程已经用完,则JOB不会执行等到下次job执行时间节点,如果项目中存在很多job执行过程耗时比时间周期还长就会导致这种情况发生。

解决办法:
1) 更具实际情况动态调整quartz线程池大小

    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.threadCount">50</prop>
            </props>
        </property>
    </bean>

在这里插入图片描述

2) 把不用并发执行的job禁止并发执行
在这里插入图片描述
禁用并发执行的job在未执行完时到达下一个开始时间不会占用一个新的线程去执行,但需要更具业务情况来确定该job是否需要并发执行。

最终,1,2结合先把总线程数调大,再将一些定时同步数据的job都改为非并发执行,解决!

  1. 修改系统时间导致
    经过测试,系统时间往后调,quartz到周期任务时间点能正常执行。
    系统时间往前调,quartz到周期时间点不执行。

问题分析:quartz默认记录的是周期的下次具体执行时间,你系统时间往前调,它记录的下次执行时间还是未来,所以到了上个周期任务执行时间,它不会执行。

解决思路:
1) quartz 是否有相关配置或接口,让支持系统时间往前调,还能正常执行? 暂未找到。
2) 系统业务代码,在启动服务时,做一个独立demon线程,判断系统时间是否往前调了,如果发现系统时间是往前调了,则重载整个quartz。

               //独立线程,判断系统时间是否发生修改
                //发生系统时间往前调情况,强制刷新加载周期任务
                Thread thread = new Thread(){
                    @Override
                    public void run(){
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        while(true){

                            //判断当前时间比上次时间少
                            Long currentTimeMillis = System.currentTimeMillis();
                            LoggerUtil.info("currentTimeMillis: " + currentTimeMillis);
                            
                            if(currentTimeMillis < lastTimeMillis){
                                System.out.println(" The system time changes to the past !");
                                taskManagerService.refreshJobTrigger();
                            }
                            lastTimeMillis = currentTimeMillis;

                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                System.out.println("InterruptedException");
                            }
                        }

                    }
                };
                thread.start();

Quartz集群

[推荐-有源码分析]Quartz集群原理分析
参考URL: https://blog.youkuaiyun.com/ym123456677/article/details/80506850

Quartz的集群模式指的是一个集群下多个节点管理同一批任务的调度,通过共享数据库的方式实现,**保证同一个任务到达触发时间的时候,只有一台机器去执行该任务。**每个节点部署一个单独的quartz实例,相互之间没有直接数据通信。

为什么使用Quartz集群

虽然单个Quartz实例能给予我们很好的任务job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间挂掉了也能确保所有的 Job 得到执行。

像之前的quartz和spring scheduler如果我们部署在单台机器上,虽然表面上我们解决了多台机器重复执行的问题,但是却有隐含的风险。单台机器如果宕机,那么定时任务功能将会导致停止,所以要部署到多台,保证系统的高可用性。并且随着我们要执行的定时任务越来越多,那么单台的压力会越大。
如果我们部署到多台机器上,那么随之而来的问题就是重复记录如何避免。那么就需要我们去了解分布式锁的实现方式。而quartz的集群就采用了mysql的行级锁互斥来防止重复记录的。

Quartz源码学习总结

quartz源码分析——执行引擎和线程模型
参考URL: https://www.cnblogs.com/liuroy/p/7517777.html
Quartz SimpleThreadPool的源码,一个简单的线程池的实现原理
参考URL: https://blog.youkuaiyun.com/u012881904/article/details/77507179
Quartz的线程池解析
参考URL: https://www.cnblogs.com/chanedi/p/4169510.html

SimpleThreadPool

Quartz SimpleThreadPool的源码,一个简单的线程池的实现原理
参考URL:https://blog.youkuaiyun.com/u012881904/article/details/77507179

quartz默认使用SimpleThreadPool 线程池,也可以自定义实现类,并为quartzProperties配置org.quartz.threadPool.class参数。
这个线程池非常简单,都没有BlockingQueue:

SimpleThreadPool中可用资源的表示

private List< WorkerThread> workers;//总的线程
private LinkedList< WorkerThread> availWorkers = new LinkedList< WorkerThread>();//可用的线程 
private LinkedList< WorkerThread> busyWorkers = new LinkedList< WorkerThread>();//工作中的线程资源

一般的线程实现无疑就是继承Thread和实现Runable两种方法

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("thread run...");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

这里的线程执行完了之后就回收了线程的资源…和我们的线程池的资源是有区别的,线程池的资源是一直保持运行的状态,没有被回收,看看WorkerThread的实现:

循环的执行Run,让线程永不停息(这样才线程的资源不会被回收),通过状态量控制线程的终止

总结:
整个代码使用wait和notify维持线程间的同步执行问题
对于共享线程的原理时使用Loop+wait+notify,当有线程执行的时候,被唤醒执行任务。

自己实现一个简化的线程池,帮助理解

SimpleThreadPool实践
参考URL: https://www.cnblogs.com/maozhige/p/5084302.html

核心思想如下:

  • 线程池实例为单例
  • 线程池实例中保存着一个线程数组,用来分发任务
  • 线程池中通过一个BlockingQueue实例,来实现FIFO的任务队列,这个实例同时被线程数组中的每一个线程拥有
  • 线程通过while循环,不断从队列中取出任务执行(Runnable

未测试。

RAMJobStore

public class RAMJobStore implements JobStore

Quartz源码——Quartz调度器的Misfire处理规则

Quartz源码——Quartz调度器的Misfire处理规则(四)
参考URL: https://blog.youkuaiyun.com/u010648555/article/details/53672738
Quartz的misfire理解
参考URL: https://blog.youkuaiyun.com/furzoom/article/details/54906263

CronTrigger的misfire机制—-默认的 Trigger.MISFIRE_INSTRUCTION_SMART_POLICY !!!

misfire用于Trigger触发时,线程池中没有可用的线程或者调度器关闭了,此时这个Trigger变为misfire。当下次调度器启动或者有可以线程时,会检查处于misfire状态的Trigger。而misfire的状态值决定了调度器如何处理这个Trigger。

对于周期性的任务,如果有misfire的情况出现,则会自动更新CronTrigger的时间周期
默认情况下会在当前时间马上执行前一个被misfire的任务
而如果设置MISFIRE_INSTRUCTION_DO_NOTHING,则不对misfire的任务做特殊处理,只从当前时间之后的下一次正常调度时间开始执行

产生misfire的前提是

到了该触发执行时上一个执行还未完成,且线程池中没有空闲线程可以使用(或有空闲线程可以使用但job设置为@DisallowConcurrentExecution)且过期时间已经超过misfireThreshold

满足以上条件就会触发quartz的misfire。

参考

Spring 整合 Quartz 分布式调度
参考URL: http://www.sohu.com/a/228004375_827544
SpringBoot集成Quartz动态定时任务
参考URL: https://blog.youkuaiyun.com/upxiaofeng/article/details/79415108

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西京刀客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值