理论基础

小顶堆

参考 https://blog.csdn.net/zhizhengguan/article/details/106826270

时间轮算法

参考 https://blog.csdn.net/yu757371316/article/details/107320068
分层时间轮

JDK定时器 Timer使用及原理

1、timer的使用

3、timer中存在的问题

timer中所有的任务都是串行执行。

4、timer的应用场景分析

  1. package com.wxl.example.timer;
  2. import java.util.Date;
  3. import java.util.Timer;
  4. import java.util.TimerTask;
  5. public class TimerTest {
  6. public static void main(String[] args) {
  7. Timer t = new Timer(); //任务启动
  8. for (int i = 0; i<1; i++) {
  9. TimerTask timerTask = new FoolTimerTask("task" + i);
  10. // t.schedule(timerTask, new Date(),2000); //任务添加 任务执行超时 丢任务
  11. t.scheduleAtFixedRate(timerTask, new Date(),2000); //任务添加 丢任务 执行时间混乱
  12. //在 run中使用定时任务线程池规避
  13. }
  14. }
  15. }
  16. class FoolTimerTask extends TimerTask {
  17. private String name;
  18. public FoolTimerTask(String name) {
  19. this.name = name;
  20. }
  21. @Override
  22. public void run() {
  23. try{
  24. System.out.println("name=" + name + ",startTime=" + new Date());
  25. Thread.sleep(3000);
  26. System.out.println("name=" + name + ",endTime=" + new Date());
  27. }catch(Exception e){
  28. e.printStackTrace();
  29. }
  30. }
  31. }

定时任务线程池解析

1、使用简介

线程池中每个独立任务串行执行,各任务执行并行
可以看到此时虽然我们指定的周期为 2 s,但是因为任务的运行就需要 3 s(超过周期),所以这种情况下 scheduleAtFixedRate 的处理方式为 上一次任务刚完成,则紧接着立即运行下一次任务,而不是使用线程池中的空闲线程来运行任务以维护 2 秒这个周期 —— 由此可见,每个定时任务在 ScheduledThreadPoolExecutor 中,都是串行运行的,即下一次运行任务一定在上一次任务结束之后。
如果某一个线程出现异常则丢弃。
如果定时任务数量超过 初始线程数量,因为小根堆的原因,leader-follower模式。
注意: DelayQueue是无界队列(maximumPoolSize参数无效);DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序,若time相同则根据sequenceNumber排序
参考: https://blog.csdn.net/xueguchen/article/details/117550455

  1. package com.wxl.example.timer;
  2. import java.util.Date;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.ScheduledExecutorService;
  5. import java.util.concurrent.TimeUnit;
  6. public class ScheduleThreadPoolTest {
  7. public static void main(String[] args) {
  8. ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
  9. // Executors.newSingleThreadExecutor();
  10. for (int i = 0; i<3; i++) {
  11. Runnable task = new Task("task" + i);
  12. // scheduledExecutorService.schedule(task, 2L, TimeUnit.SECONDS); //执行一次
  13. scheduledExecutorService.scheduleAtFixedRate(task, 0,2, TimeUnit.SECONDS);
  14. // 可以看到此时虽然我们指定的周期为 2 s,但是因为任务的运行就需要 3 s(超过周期),
  15. // 所以这种情况下 scheduleAtFixedRate 的处理方式为 上一次任务刚完成,则紧接着立即运行下一次任务,
  16. // 而不是使用线程池中的空闲线程来运行任务以维护 2 秒这个周期 —— 由此可见,每个定时任务在 ScheduledThreadPoolExecutor 中,
  17. // 都是串行运行的,即下一次运行任务一定在上一次任务结束之后。
  18. }
  19. }
  20. }
  21. class Task implements Runnable {
  22. private String name;
  23. public Task(String name) {
  24. this.name = name;
  25. }
  26. @Override
  27. public void run() {
  28. try{
  29. System.out.println("name=" + name + ",startTime=" + new Date());
  30. Thread.sleep(3000);
  31. System.out.println("name=" + name + ",endTime=" + new Date());
  32. }catch(Exception e){
  33. e.printStackTrace();
  34. }
  35. }
  36. }

2、Leader-Follower模式

Quartz - 图1
多线程网络模型:

线程会有三种身份中的一种:leader和follower,以及一个干活中的状态:proccesser

基本原则:永远最多只有一个leader,而所有follower都在等待成为leader。

线程池启动时会自动产生一个Leader负责等待网络IO事件,当有一个事件产生时,Leader线程首先通知一个Follower线程将其提拔为新的Leader,然后自己执行这个任务,去处理这个网络事件,处理完毕后加入Follower线程等待队列,等待下次成为Leader。

这种方法可以增强CPU高速缓存相似性,及消除动态内存分配和线程间的数据交换。

3.ScheduledThreadPoolExecutor与Timer的区别

  1. Timer是单线程模式,ScheduledThreadPoolExecutor是多线程模式,并且重用线程池
  2. Timer调度是基于操作系统的绝对时间的,ScheduledThreadPoolExecutor调度是基于相对时间的,不受操作系统时间改变的影响
  3. Timer不会捕获TimerTask抛出的异常,加上Timer又是单线程的。一旦某个调度任务出现异常,则整个线程就会终止,其他需要调度的任务也不再执行。ScheduledThreadPoolExecutor基于线程池来实现调度功能,某个任务抛出异常后,其他任务仍能正常执行。
  4. Timer中执行的TimerTask任务整体上没有优先级的概念,只是按照系统的绝对时间来执行任务。ScheduledThreadPoolExecutor距离下次执行的时间间隔短的任务的优先级比较高
  5. Timer不支持对任务的排序。ScheduledThreadPoolExecutor为需要调度的每个任务按照距离下次执行时间间隔的大小来排序
  6. Timer无法从TimerTask中获取返回的结果。ScheduledThreadPoolExecutor中FutureTask类,能够通过Future来获取返回的结果。

定时任务框架-quartz

1、使用简介

2、各组件介绍

3、组件关系架构分析

4、监听器及插件

5、springboot整合quartz

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-quartz</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.quartz-scheduler</groupId>
  7. <artifactId>quartz</artifactId>
  8. <version>${quartz.version}</version>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.quartz-scheduler</groupId>
  12. <artifactId>quartz-jobs</artifactId>
  13. <version>${quartz.version}</version>
  14. </dependency>
  1. @RequestMapping("/test")
  2. public void test() {
  3. TriggerKey triggerKey = TriggerKey.triggerKey("trigger1", "trigger1");
  4. //确保触发器在调度器里是唯一的
  5. if (scheduler == null) {
  6. System.out.println("注入失败");
  7. return;
  8. }
  9. try {
  10. Trigger trigger = scheduler.getTrigger(triggerKey);
  11. if (trigger == null) {
  12. trigger = TriggerBuilder.newTrigger()
  13. .withIdentity(triggerKey)
  14. .withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?"))
  15. .build();
  16. }
  17. JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
  18. .withIdentity("job1","job1")
  19. .usingJobData("name", "测试调度")
  20. .build();
  21. scheduler.scheduleJob(jobDetail,trigger);
  22. scheduler.start();
  23. } catch (SchedulerException e) {
  24. e.printStackTrace();
  25. }
  26. }