前置理论:

1.小顶堆(适合任务少的,因为向下调整耗费性能)

堆:是一完全二叉树(除了最后一层节点其他层都达到最大节点数,且最后一层都靠左排列);堆中某个节点的值总不大于或不小于其父节点。

定时任务是基于小顶堆。每一个job都对应每一个节点,节点值里面存储job的到期时间(Delay),每次取堆顶的节点去执行。

插入元素:插入尾部,然后上浮(每次与n/(2^x)的位置济宁比较,时间复杂度O(log n)。

删除堆顶元素:将尾部元素放到堆顶,然后下浮。第一个任务(最近)执行后(堆顶元素)就进行这个操作。

2.时间轮算法(适合任务多)

*链表或者数组实现时间轮询:while-true-sleep。遍历数组,每个下标放置一个链表,链表节点放置任务,遍历到了就取出执行。

*round型时间轮询:任务上记录一个round,遍历到了就将round减一,为0时取出执行(需要遍历所有的任务,效率较低)。

*分层时间轮(cron表达式):使用多个时间维度的轮,天轮:记录几点执行,月轮:记录几号执行。月轮遍历到了,将任务取出放去天轮里面,即可实现几号几点执行。

一、JDK定时器:Timer使用及原理分析

1.timer的使用

2.timer的数据结构和原理分析

*TaskQueue:小顶堆,存放timeTask

*TimerThread:任务执行线程,死循环不断检查是否有任务需要开始执行,有就执行,还是在这个线程执行。

*单线程执行任务,会阻塞。Scheaule:任务执行超时,会导致后面的任务往后推移。预想在这个间隔执行的任务就没了;ScheduleAtFixedRate:任务超时可能导致下一个任务马上执行。

*运行时异常会导致timer线程终止

*任务调度是基于绝对时间的,对系统时间敏感。

3.timer中存在的问题

4.timer的应用场景分析

/*** Timer:* 放入queue(小堆)中去执行* 底层task调用run方法,单线程执行,任务时长超过间隔时间会乱* 单线程:任务阻塞,导致超时,解决:使用线程池*/
public class TimerTest {public static void main(String[] args) {Timer timer = new Timer();  //任务启动for (int i = 0; i < 2 ;i++){TimerTask task = new FoolTimerTask("foo" + i);//添加任务/*** schedule:真正的执行时间,取决于上一个任务的结束时间 (任务超过间隔时间就会丢失任务)* scheduleAtFixedRate:严格按照预设时间 (任务超过间隔时间执行时间会乱)*/timer.schedule(task,new Date(),2000);  //任务,启动时间,间隔时间}}
}
class FoolTimerTask extends TimerTask{private String name;public FoolTimerTask(String name) {this.name = name;}@Overridepublic void run() {long startTimer = System.currentTimeMillis();try {Thread.sleep(3000);//使用线程池避免任务阻塞} catch (InterruptedException e) {e.printStackTrace();}long endTimer = System.currentTimeMillis();System.out.println(endTimer-startTimer);}
}

二、定时任务线程池解析

1.使用简介

2.单线程版和多线程版

单线程见上面代码

3.数据结构、原理分析

*ScheduledThreadPoolExecutor:

使用多线程执行任务,不会相互阻塞

如果线程失活,会新建线程执行任务(线程抛异常,任务会被丢弃,需要捕获异常)

DelayWorkQueue:小顶堆、无界队列

1.在定时线程池中,最大线程数是没有意义的

2.执行时间距离当前时间越近的任务就在队列的最前面

3.用于添加ScheduleFutureTask(继承于FutureTask,实现RunnableScheduleFuture接口)

4.线程池中的线程从DelayQueue中获取ScheduleFutureTask执行

5.实现了Delay接口,可以通过getDelay方法来获取延迟时间

*SingleScheduledThreadPoolExecutor:

单线程的线程池:适用于单个后台的线程执行周期,同时又保证任务顺序执行。

ScheduleThreadPoolExecutor

//一定延迟之后只执行一次某个任务

public ScheduleFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

public <V> ScheduleFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);// 会有返回值

//一定延迟之后周期性的执行某个任务

//间隔是固定的,无论上一个任务是否执行完成

public ScheduleFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

//时间是不固定的,其会在周期任务的上一个任务执行完成之后才开始计时,并在指定的时间间隔之后才开始执行任务

public ScheduleFuture<?> scheduleAtSicedRate(Runnable command, long initialDelay, long delay, TimeUnit unit);

4.应用场景

适用于多个后台线程执行周期性任务,同时为了满足资源管理的需求限制后台的线程数

5.Leader-Follower模式

避免没必要的唤醒和阻塞的操作,所有的工作线程中只有一个leader线程,其他线程都是follower线程,都处理休眠中,当leader线程拿到任务后执行任务前,自己会变成follower线程,同时会选出新的leader来,才去执行任务。如果此时有下一个任务,就是新的leader线程来执行,并以此往复这个过程,当之前执行任务的线程执行完毕再回来时,会判断如果此时已经没有任务了,又或者有任务但是其他的线程作为leader线程,那么自己就休眠了,如果此时有任务但是没有leader线程,那么自己就会重新成为leader线程来执行任务。

/*** 基于线程池的Timer:* 解决单线程阻塞*/
public class TimerPool {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);for (int i = 0; i < 2; i++) {scheduledThreadPool.schedule(new Task("task-" + i),2, TimeUnit.SECONDS);scheduledThreadPool.scheduleAtFixedRate(new Task("task-" + i),0,2, TimeUnit.SECONDS);}}
}class Task implements Runnable {private String name;public Task(String name) {this.name = name;}public void run() {long startTimer = System.currentTimeMillis();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}long endTimer = System.currentTimeMillis();System.out.println(endTimer - startTimer);}
}

三、定时任务框架-quartz

1.使用简介

2.各组件介绍

Job:

封装成JobDetail设置属性,

@DisallowConcurrentExecution:禁止并发地执行同一个job定义(jobDetail定义的)的多个实例

@PersistJobDataAfterExectuion:持久化jobDetail中的JobDataMap(对trigger中的datamap无效)

如果一个任务不是持久化的,泽当没有触发器关联它的时候,Quartz会从Scheduler中删除它

如果一个任务请求恢复,一般是该任务执行期间发生了系统崩溃或者其他关闭进程的操作,当服务再次启动的时候,会再次执行该任务,此时,JobExecutionContext.isRecovdering()会返回true

Trigger:触发器,指定执行时间,开始结束时间等

*优先级:同时触发的trigger之间会比较优先级,如果trigger是可恢复的,在恢复之后再调度,优先级不变

*misfire,错误触发:

1.判断misfire的条件:job到达触发时间时没有被执行,被执行的延迟时间超过了Quartz配置的misfireThreshold阈值

2.产生的原因:当job达到触发时间时,所有线程都被其他job占用,没有可用线程;在job需要触发的时间点,schedule停止了;job使用了@DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没有执行完;job指定了过去的开始时间

3.策略:默认使用MISFIRE_INSTRUCTION_SMART_POLICY策略;

SimpleTrigger:now*相关的策略,会立即执行第一个misfire的任务,同时修改startTime和repeatCount,因此会重新计算finalFireTime,原计划执行时间会被打乱;next*相关策略,不会立即执行misfire的任务,也不会修改startTime和repeatCount,因此finalFireTimeu也没有改变,发生了misfire也还是按照原计划执行

CronTrigger:MISFIRE_INSTRUCTION_IGNORE_SMART_POLICY(Quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按原计划执行);MISFIRE_INSTRUCTION_FIRE_ONCE_NOW(立即执行第一个发生misfire的任务,忽略其他发生misfire的任务,然后按原计划执行);MISFIRE_INSTRUCTION_DO_NOTHING(所有发生misfire的任务都被忽略,只是按照原计划执行)

*clendar:用于排除时间段

*SimpleTrigger:具体时间点,指定间隔重复执行

*CronTrigger:cron表达式

Schedule:调度器,基于trigger的设定执行job

*SchedulerFactory:

创建Scheduler;

DirectScheduleFactory:在代码里定制Schedule参数;

StdScheduleFactory:读取classpath下的quartz.properties配置来实例化Schedule

*jobStore:

存储运行时inxi的,包括Trigger,Scheduler,jobDetail,业务锁等

RamJobStore(内存实现)

JobStoreTX(JDBC,事务由Quartz管理)

JobStoreCMT(JDBC,使用容器事务)

ClusteredJobStore(集群实现)

TerracottaJobStore(Terracotta中间件)

*ThreadPool:SimpleThreadPool;自定义线程池

JobDataMap:保存任务实例的状态信息

jobDetail:默认只在Job被添加到调度程序scheduler的时候,存储一次关于该任务的状态信息数据,可以使用注解@PersisJobDataAfterExecution注解标明在一个任务执行完毕之后就存储一次

trigger:任务被多个触发器引用的时候,根据不同的触发时机,可以提供不同的输入条件

3.组件关系架构分析

4.监听器及插件

5.java项目整合quartz

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>
/*** @DisallowConcurrentExecution:禁止并发地执行同一个job定义(jobDetail定义的)的多个实例* @PersistJobDataAfterExectuion:持久化jobDetail中的JobDataMap(对trigger中的datamap无效)* Scheduler每次执行,都会根据JobDetail创建一个新的job实例,这样就可以规避并发访问的问题*/
@DisallowConcurrentExecution
public class MyJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("MyJob executr:" + System.currentTimeMillis());//可以从缓存的Mao里面获取数据,同名会覆盖JobDataMap jobDetailMap = context.getJobDetail().getJobDataMap();JobDataMap triggerMap = context.getTrigger().getJobDataMap();JobDataMap mergeMap = context.getMergedJobDataMap();}
}
public class TestJob {public static void main(String[] args) {JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1","group1")  //设置job的名字和组.usingJobData("job","jobDetail")  //可以放入信息数据,在业务逻辑中获取.requestRecovery(true)  //job可恢复,在其执行的时候,scheduler发生硬关闭,则当scheduler重新启动的时候,该job会被重新执行,此时,该job的JobExecutionContext.isRecovering()返回true.build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "trigger1").usingJobData("trigger","trigger").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever() //也可以用cron).build();try {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();scheduler.scheduleJob(jobDetail,trigger);scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}}
}

四、springboot+quartz实现集群部署

注意:重启需要清除数据库中quartz相关表中信息,不然misfire会导致执行出错

pom

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

配置文件:spring-quartz.properties

//spring-quartz.properties#===========================================================
#配置JobStore
#===========================================================#JobDataMaps是否都为String类型,默认false
org.quartz.jobStore.useProperties=false
#表的前缀,默认QRTZ
org.quartz.jobStore.tablePrefix = QRTZ
#是都加入集群
org.quartz.jobStore.isClustere = true
#调度实例失效的检查时间间隔ms
org.quartz.jobStore.clusterCheckinInterval = 5000
#当设置为true时,此属性告诉Quartz在非托管JDBC连接上调用setTransactionIsolation
org.quartz.jobStore.txIsolationLevelReadCommitted = true
#数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quratz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库#===========================================================
#Scheduler 调度器属性配置
#===========================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName = ClusterQuartz
#ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceId = AUTO#===========================================================
#配置ThreadPool
#===========================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足所有客需求)
org.qquartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#指定线程数,一般设置为1-100之间的整数,根据系统资源配置
org.qquartz.threadPool.threadCount = 5
#设置线程的优先级
org.qquartz.threadPool.threadPriority = 5

调度器配置:

/*** 配置器调度类*/
@Configuration
public class SchedulerConfig {@Autowiredprivate DataSource dataSource;@Beanpublic Scheduler scheduler() throws IOException {return schedulerFactoryBean().getScheduler();}@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setSchedulerName("cluster_scheduler");  //设置调度器名字factory.setDataSource(dataSource);  //设置数据源factory.setApplicationContextSchedulerContextKey("application");  //在IOCbean中的名字factory.setQuartzProperties(quartzProperties());  //设置配置文件factory.setTaskExecutor(schedulerThreadPool());  //设置线程池factory.setStartupDelay(0);  //设置执行delayreturn factory;}/*** 获取properties*/@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/spring-quartz.properties"));propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}/*** 获取线程池配置*/@Beanpublic Executor schedulerThreadPool(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());  //取主机的核心线程数executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());executor.setQueueCapacity(Runtime.getRuntime().availableProcessors());return executor;}
}

springboot启动监听器:

/*** springboot项目启动监听器* 注意重启需要清除数据库的定时任务表信息,不然misfire会出错触发*/
@Component
public class StartApplicationListener implements ApplicationListener<ContextClosedEvent> {@Autowiredprivate Scheduler scheduler;@Overridepublic void onApplicationEvent(ContextClosedEvent event) {//定义触发器,注意同意触发器只能存在一个实例TriggerKey triggerKey = TriggerKey.triggerKey("trigger1", "group1");try {Trigger trigger = scheduler.getTrigger(triggerKey);if (trigger == null) {trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).startNow()  //设置立即启动,可不用.build();JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity("job1", "group1").build();//开始调度scheduler.scheduleJob(jobDetail, trigger);scheduler.start();}} catch (SchedulerException e) {e.printStackTrace();}}
}

业务代码:

/*** springboot 整合quartz*/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {try{//打印任务相关信息String schedulerInstanceId = context.getScheduler().getSchedulerInstanceId();String name = context.getJobDetail().getKey().getName();//调度任务相关业务代码//TODO:...}catch (Exception e){e.printStackTrace();}}
}

find by:BV1WL4y177up

SpringBoot+Quartz动态管理定时任务相关推荐

  1. ThreadPoolTaskScheduler实现动态管理定时任务

    最近,有个项目有需要用到定时任务,所以做了一个动态管理定时任务的模块.本文将从项目背景.需求.选型.思路.具体实现等方面展开介绍. 背景:有个支付类的项目,中间会产生一些中间态的订单,需要有个定时任务 ...

  2. quartz 动态添加job_SpringBoot+Quartz实现动态管理定时任务

    前言: 最近在做Java开发对接微信公众平台之类的工作,在开发过程中遇到一个需求是定时任务这块的,但是普通的定时任务还远远不能满足:最后要实现的效果是每个任务都是不同的定时时间来执行而且是在前台页面上 ...

  3. springboot quartz 动态配置_springboot集成quartz实现动态任务调度

    quartz是一个开源的作业调度框架,本文就是介绍下springboot框架下继承quartz的一些使用示例 首先我们需要添加quartz的spring-boot-starter-quartz依赖 o ...

  4. java定时执行sql语句_spring中使用quartz动态添加定时任务执行sql

    系统用来每天插入视图数据... 一.数据库表设计 1.接口配置表(t_m_db_interface_config) 2.接口日志表(t_m_db_interface_log) 3.前端配置页面 查询页 ...

  5. boot-admin整合Quartz实现动态管理定时任务

    淄博烧烤爆红出了圈,当你坐在八大局的烧烤摊,面前是火炉.烤串.小饼和蘸料,音乐响起,啤酒倒满,烧烤灵魂的party即将开场的时候,你系统中的Scheduler(调试器),也自动根据设定的Trigger ...

  6. springboot quartz动态任务处理

    现实中的很多定时任务并不像网上的那种写法那么简单都是直接通过注解的形式实现(@Scheduled(fixedRate = 10000)).我就遇到了从数据库读取相关的数据(可能有n条)然后根据数据的触 ...

  7. SpringBoot JAVA 动态设置定时任务执行时间

    一 .核心代码 @Component @EnableScheduling public class ScheduleService implements SchedulingConfigurer {p ...

  8. 定时任务系列(3)-Quartz动态定时任务

    初步认识动态任务 Quartz定时任务框架经常用于系统后台业务异步处理.平常我们使用时,主要是通过手工编写配置代码文件方式添加修改定时任务,然后重启系统.有时候我们需要根据业务运营需要,动态添加修改定 ...

  9. 定时任务动态管理-Scheduled

    文章目录 前言 一.架构流程图 二.代码实现流程 1.引入库 2.代码流程 前言 定时任务动态管理分为两种方式: 方式一:Web前台配置Trigger触发器(关联Cron).ThreadPoolTas ...

最新文章

  1. 哪些是我们必须要会深度学习知识
  2. 题目1507:不用加减乘除做加法
  3. PaperWeekly 第二十三期 --- 机器写诗
  4. 按照RFC3984协议实现H264视频流媒体 RTSP H264
  5. 一年级学情分析计算机,小学一年级语文学情分析范文
  6. 修改tomcat服务器默认端口
  7. CAN笔记(22) 特殊协议
  8. Unity3D AssetBundle相关
  9. 不同的智能门锁解决方案开锁方式
  10. 免费开源51单片机个人课程设计--基于stc89c52及红外遥控的测温智能电风扇
  11. 建立自己公众号题库系统
  12. 求职过程中展示您的社交技巧
  13. vbs模拟post请求上传文件
  14. kettle-xml输入
  15. eclipse查看安装了哪些插件
  16. OFD格式文如何打开,可以转成PDF吗?
  17. centos配置linuxptp
  18. 应用pagehelper实现大屏展示自动换页
  19. Linux JKD1.8 安装及配置
  20. php做引流脚本,引流脚本效果极速引流脚本分享

热门文章

  1. 缩小图片占用的空间大小
  2. 原码、反码、补码,带你深入理解计算机的补码运算原理(附图片解说过程),为什么计算机中数据要以补码的形式存储呢?解释补码运算规则的合理性。
  3. golang API json,struct结构中标签(Tag)的使用
  4. 百度2016校园招聘-开发测试工程师-在线编程题1-商队运输费
  5. Pandas中Series结构的切片详解以及常用技巧
  6. Android 浅尝Tinker微信热修复
  7. 曾国藩经典人生哲理语录——大师级别啊
  8. js通过浏览器调用摄像头并实现拍照
  9. 实战丨用云开发快速构建最美AI毕业照小程序
  10. 电脑故障检查不完全流程图