本文主要讲解以下几个方面:

1.定时任务的定义及其常见的模式

2.springboot集成quart实例

3.中途会遇到的一些问题

一、定时任务的定义及其常见的模式

1)定时任务的定义

首先要明白的是,定时任务在系统中的表现形式和在我们口中常说的定时是不一样的:

口中的定时:在明天的八点把设备打开,晚上八点把设备关掉,这个月每天都这样。

系统的定时:上述的定时在系统中就会表现为两个任务:

2019-05-15到2019-05-31,在8点把设备打开,间隔24小时(隔天)再操作一次;

2019-05-15到2019-05-31,在20点把设备关闭,间隔24小时(隔天)再操作一次。

也就是说系统的实现需要肢解到单个操作,然后看单个操作的规律来设定任务。(讲这个是因为涉及到后面的Cron表达式,一个时间段的可能需要肢解为多个任务)

2)常见的几种定时任务方式

一种是while无限循环,隔一定时间执行一次;

一种是Timer,支持一次性调度和循环调度,循环调度有分为固定速率调度(fixRate)和固定时延调度(fixDelay),能支持很多的应用了;它是单线程的,原理如下:

Timer 类里包含一个任务队列和一个异步轮训线程:

任务队列里容纳了所有待执行的任务,所有的任务将会在这一个异步线程里执行;

轮训线程会每次轮训出时间点最近的并且到点的任务来执行

可参考:https://blog.51cto.com/13732225/2331530

一种是ScheduledExecutorService,这个是用线程池了,可以很灵活的去设定第一次执行任务delay时间等,

可参考:https://www.cnblogs.com/maoniu602/p/3900587.html

一种就是quartz,基本上具备了上述的各种优点,还实现了任务和触发器的多对多的关系,可以动态的添加删除定时任务,另外很好的支撑集群调度;它的核心如下:

1个job可对应多个Trigger

二、springboot集成quart实例

主要的结构如下:

netak可放到model目录中。

这是我的线程启动类,用来替代SchedulerListener.java,大家没有其他线程也可以用SchedulerListener,自己看着放。

1.QuartzScheduler.java:涉及的是基本的配置,以后的动态修改方法。这个是核心,大家好好看看注解。


import java.util.Date;
import java.util.List;import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;@Configuration
public class QuartzScheduler {@Autowiredprivate Scheduler scheduler;@Autowiredprivate NeTaskService neTaskService;/*** 开始执行所有任务*/public void startJob() throws SchedulerException {//启动时进行查库将任务添加至jobList<NeTask> netaskList = neTaskService.findAllByNeIdAndInUse();List<NeTask> zctaskList = neTaskService.findAllByIpAndInUse();//添加任务至调度器schedulerstartJob1(scheduler,netaskList);//调度任务开始执行//      scheduler.start();startJob2(scheduler,zctaskList);scheduler.start();}/** 重启所有任务*/public void restartJob() throws SchedulerException, InterruptedException {//不可以用shutdown,也不需要停止,直接清除,然后启动//       scheduler.shutdown();//     scheduler.pauseAll();scheduler.clear();this.startJob();}/*** title:* mentality:* @throws * @param scheduler2* @param zctaskList*/private void startJob2(Scheduler scheduler2, List<NeTask> zctaskList) throws SchedulerException{// TODO Auto-generated method stub}/*** title:计划任务1* mentality:* @throws * @param scheduler2* @param netaskList*/private void startJob1(Scheduler scheduler2, List<NeTask> netaskList) throws SchedulerException{// TODO Auto-generated method stub// 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例// JobDetail 是具体Job实例for(NeTask netask : netaskList){JobDetail jobDetail = JobBuilder.newJob(NeTaskJob.class)//不同的业务,增加不同的.class.withIdentity(netask.getId().toString(), netask.getStationId()+netask.getNeId()).build();jobDetail.getJobDataMap().put("id",netask);List<Cron> cronList = CronUtil.getCronByTask(netask);
//          for(Cron cron : cronList ){for(int i = 0;i< cronList.size();i++){// 基于表达式构建触发器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(//下面的cron你可以直接写个cron表达式来做验证,入:每隔5秒执行一次:*/5 * * * * ?cronList.get(i).getCron());// CronTrigger表达式触发器 继承于Trigger// TriggerBuilder 用于构建触发器实例CronTrigger cronTrigger = TriggerBuilder.newTrigger()//若一个jobdetail有多个trigger,则需要注意命名规则,便于后面修改任务
//                      .withIdentity(netask.getNeId().toString(), netask.getStationId()).forJob(jobDetail).withIdentity(netask.getId() + CronUtil.cronEndId[i], netask.getStationId()+netask.getNeId()).withSchedule(cronScheduleBuilder).build();// scheduleJob该接口的作用是在将任务加入Quartz的同时绑定一个Trigger,Quartz会在加入该任务后自动设置Trigger的JobName与JobGroup为该JobDetail的name与groupif(i==0){scheduler2.scheduleJob(jobDetail, cronTrigger);//第一次必须有jobdetail}else{scheduler2.scheduleJob(cronTrigger);}//rescheduleJob(String, String, Trigger)  替换一个指定的Trigger, 即解除指定Trigger与任务的绑定,并将新的Trigger与任务绑定,Quartz会自动调整新Trigger的JobName与JobGroup,而旧的Trigger将被移除//Scheduler#triggerJob(String, String)   创建一个立即触发的Trigger,并将其与name与group指定的任务绑定}}}/*** 获取Job信息** @param name* @param group* @return* @throws SchedulerException*/public String getJobInfo(String name, String group) throws SchedulerException {TriggerKey triggerKey = new TriggerKey(name, group);CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),scheduler.getTriggerState(triggerKey).name());}/*** 修改某个任务的执行时间* (修改的是具体的trigger,不是jobdetail)* @param name* @param group* @param time* @return* @throws SchedulerException*/public boolean modifyJob(String name, String group, String time) throws SchedulerException {Date date = null;TriggerKey triggerKey = new TriggerKey(name, group);CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);String oldTime = cronTrigger.getCronExpression();if (!oldTime.equalsIgnoreCase(time)) {CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(cronScheduleBuilder).build();date = scheduler.rescheduleJob(triggerKey, trigger);}return date != null;}/*** 暂停所有任务** @throws SchedulerException*/public void pauseAllJob() throws SchedulerException {scheduler.pauseAll();}/*** 暂停某个任务** @param name* @param group* @throws SchedulerException*/public void pauseJob(String name, String group) throws SchedulerException {JobKey jobKey = new JobKey(name, group);JobDetail jobDetail = scheduler.getJobDetail(jobKey);if (jobDetail == null){return;}scheduler.pauseJob(jobKey);}/*** 恢复所有任务** @throws SchedulerException*/public void resumeAllJob() throws SchedulerException {scheduler.resumeAll();}/*** 恢复某个任务** @param name* @param group* @throws SchedulerException*/public void resumeJob(String name, String group) throws SchedulerException {JobKey jobKey = new JobKey(name, group);JobDetail jobDetail = scheduler.getJobDetail(jobKey);if (jobDetail == null){return;}scheduler.resumeJob(jobKey);}/*** 删除某个任务** @param name* @param group* @throws SchedulerException*/public void deleteJob(String name, String group) throws SchedulerException {JobKey jobKey = new JobKey(name, group);JobDetail jobDetail = scheduler.getJobDetail(jobKey);if (jobDetail == null){return;}scheduler.deleteJob(jobKey);}
}

2.NeTaskJob.java:任务类,这里的关键是@PostConstruct,这是我们在实际项目中要调用其他接口的时候要用到的。

ZcTaskJob.java就是另一个任务,格式都一样,就里调用的接口不一样,这里就不写了。


import javax.annotation.PostConstruct;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class NeTaskJob implements Job{@Autowiredprivate NeThreadService neThreadService;public static NeTaskJob neTaskJob;public NeTaskJob(){}
//注入要调用的方法@PostConstructpublic void init(){neTaskJob = this;neTaskJob.neThreadService = this.neThreadService;}// private void before(){
//        System.out.println("任务开始执行");
//    }/* (non-Javadoc)* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)*/@Overridepublic void execute(JobExecutionContext JobExecutionContext) throws JobExecutionException {// TODO Auto-generated method stub
//      before();System.out.println("任务开始:"+System.currentTimeMillis());// TODO 业务:此处是关键,如何从map中取得对应的job名称(netask的唯一标识id)NeTask netask = (NeTask)JobExecutionContext.getMergedJobDataMap().get("id");//实际项目中经常要调用到其他的接口方法,那么一定要在上方注入PostConstructneTaskJob.neThreadService.sendCmd(netask.getPeriodValue());System.out.println("任务结束:"+ netask.getTitle() +System.currentTimeMillis());
//        after();}//   private void after(){
//        System.out.println("任务开始执行");
//    }}

3.service就是大家自己从数据库取任务,更新任务的方法,就不写了。

4.listener

@Component
public class AppListener implements ApplicationListener<ApplicationEvent>{private static boolean loaded = false;@Autowiredprivate QuartzScheduler quartzScheduler;@Overridepublic void onApplicationEvent(ApplicationEvent e){if(e instanceof ContextRefreshedEvent){if(!loaded){//避免多次执行loaded = true;//定时任务启动try {quartzScheduler.startJob();System.out.println("任务已经启动...");} catch (SchedulerException se) {se.printStackTrace();}}if(e instanceof ContextStartedEvent){}}}/*** 初始注入scheduler* @return* @throws SchedulerException*/@Beanpublic Scheduler scheduler() throws SchedulerException{SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();return schedulerFactoryBean.getScheduler();}}

三、中途会遇到的一些问题

1.The job (0000000035.2) referenced by the trigger does not exist.

2.Jobs added with no trigger must be durable.

这些问题都是job配置多个触发器时需要注意的问题:

第一次scheduler2.scheduleJob(jobDetail, cronTrigger);必须有jobDetail第二个trigger不能有jobDetail,scheduler2.scheduleJob(cronTrigger);,同时注意在newTrigger的时候必须.forJob(jobDetail)指明触发器是给哪个job用的,范例见QuartzScheduler.java;

此致,关于配置的问题就说这么多。关于实际应用中集成cron的问题,我们下篇再说。

在做的过程中筛选出来的几个对我参考价值较高的链接如下:

quartz:

https://www.cnblogs.com/qlqwjy/p/8721982.html

https://blog.csdn.net/weixin_40692498/article/details/88025275

https://blog.csdn.net/upxiaofeng/article/details/79415108#commentBox

https://blog.csdn.net/qq_28483283/article/details/80623417#commentBox

https://blog.csdn.net/u013042707/article/details/82934725#commentBox

https://blog.csdn.net/liuchuanhong1/article/details/60873295

https://blog.csdn.net/qq_29145405/article/details/81843123

cron:

https://www.cnblogs.com/a8457013/p/8515939.html

定时任务:springboot集成Quartz实现多任务多触发的动态管理相关推荐

  1. java quartz 动态执行,浅谈SpringBoot集成Quartz动态定时任务

    SpringBoot自带schedule 沿用的springboot少xml配置的优良传统,本身支持表达式等多种定时任务 注意在程序启动的时候加上@EnableScheduling @Schedule ...

  2. Springboot集成quartz定时任务可视化配置​​​​​​​

    转自我的个人博客:Springboot集成quartz定时任务可视化配置 使用quartz定时任务已经有一段时间了,今天记录一下Springboot 2.x集成Quartz. 1.引入quartz j ...

  3. SpringBoot集成Quartz(定时任务)

    SpringBoot集成Quartz(定时任务)_鱼找水需要时间的博客-CSDN博客_springboot集成quartz

  4. SpringBoot集成quartz定时调度任务并通过JDBC持久化

    SpringBoot集成quartz定时调度任务并通过JDBC持久化 话不多说上干货 项目pom依赖 配置类 抽象出调度任务实体类 调度执行和调度任务更改工具类 调度配置与执行的代码完毕,下面就是对持 ...

  5. SpringBoot集成Quartz框架

    SpringBoot集成Quartz框架 (一)集成环境: ​ Win10系统 ​ JDK版本:11.0.13 ​ SpringBoot版本:2.3.4.RELEASE ​ Quartz版本:2.3. ...

  6. SpringBoot集成Quartz(解决@Autowired空指针Null问题即依赖注入的属性为null)

    SpringBoot集成Quartz(解决@Autowired空指针Null问题即依赖注入的属性为null) 参考文章: (1)SpringBoot集成Quartz(解决@Autowired空指针Nu ...

  7. SpringBoot - 集成Quartz框架:Couldn‘t acquire next trigger: Couldn‘t retrieve trigger: 不良的类型值 long : \x

    写在前面 SpringBoot 集成Quartz框架时,数据保存方式使用PostgreSQL进行数据库持久化. 报错如下: Couldn't acquire next trigger: Couldn' ...

  8. SpringBoot集成Quartz动态定时任务

    SpringBoot自带schedule 沿用的springboot少xml配置的优良传统,本身支持表达式等多种定时任务 注意在程序启动的时候加上@EnableScheduling @Schedule ...

  9. SpringBoot集成Quartz实现定时任务

    1.Quartz官网 http://www.quartz-scheduler.org/ Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中-从最小的独立应用程序到最大的电子商 ...

最新文章

  1. 给Ubuntu添加清华的软件源
  2. split函数python 未定义_Python之Split函数
  3. 用数据驱动思想来设计游戏-读《游戏编程精粹1》
  4. 【Linux驱动】linux内核模块简介
  5. [kubernetes] 资源管理 ---- 资源请求和限制
  6. 前端学习(3000):vue+element今日头条管理--封装请求模块
  7. JavaScript依赖注入的实现思路
  8. HDU 5586 Sum (预处理 + 动态规划)
  9. 开放计算机应用基础形考3,国家开放大学《计算机应用基础》考试与答案形考任务模块3 模块3 Excel 2010 电子表格系统—客观题答案.doc...
  10. mysql二进制日志管理
  11. javaWeb项目添加hibernate教程
  12. MIPI归纳---为什么阻抗为100欧姆
  13. 人工智能实战第六次作业_张绍恺
  14. 缓存问题(二) 布隆过滤器(Bloom Filter) 介绍和原理
  15. 毕业4年年薪200万是怎样的一种体验?
  16. Android自定义日历控件
  17. 【教程】利用github学生认证免费使用CLion一年
  18. linux性能监控工具-nmon安装使用详细教程
  19. MT2502 datasheet,MT2502硬件设计,MT2502芯片资料
  20. ug产品摆正高级技巧_UG NX如何摆正产品零件模型

热门文章

  1. P2178 后缀数组 + 并查集
  2. python tkinter界面 多进程启动scrapy爬取百度贴吧的回复,显示爬取进度,并可以搜索回帖人,指定时间生成词云图,用pyinstaller打包成exe(七)
  3. C Primer Plus 第02章 C语言概述 学习笔记及复习题、编程题解答
  4. 为什么单线程的Redis如此的快(Why is single-threaded Redis so fast)
  5. B站最专业的DC漫威UP主,深度挖掘漫威故事内容。
  6. Android-jni(10)-jni调用java父类方法
  7. 将视频文件旋转90°的方法
  8. 汉语属于哪个语系_汉语,日语,韩语分别属于什么语系?
  9. 【微信小程序】rpx
  10. Why Would I Ever