定时器的几种常用方案及原理

常见方案

  • 单机:JDK的Timer、ScheduledThreadPoolExecutor,及Spring的@Scheduled;
  • 分布式:Quartz;

JDK Timer

实现原理: 每个定时器实例绑定一个消费线程和任务队列,业务线程提交定时任务到任务队列,任务按照下次执行时间在队列中排序;消费线程每次从队首获取任务,查看是否超过执行时间点,超过则立马执行,否则await等待,如果是周期任务则再计算下次执行时间点,将任务插入队列,如下图所示:
定时器的几种常用方案及原理_第1张图片
优缺点: Timer为单线程模式,当某个任务很耗时时容易导致后续待执行的任务收到影响,并且如果任务执行异常也没有捕获,导致线程终止运行,因此除了一些特别简单的场景,生产环境不建议使用;

ScheduledThreadPoolExecutor

  实现原理:每个定时器实例绑定一个消费线程池和任务队列,业务线程提交定时任务到任务队列,任务按照下次执行时间在队列中排序;消费线程池中的线程每次从队首获取任务,查看是否超过执行时间点,超过则立马执行,否则await等待,如果是周期任务则再计算下次执行时间点,将任务插入队列;

  • 创建实例:配置线程池和创建任务队列;
    在这里插入图片描述
  • 提交任务:按照下次执行时间插入队列;
    定时器的几种常用方案及原理_第2张图片
    定时器的几种常用方案及原理_第3张图片
  • 消费线程:获取任务,执行任务;
    定时器的几种常用方案及原理_第4张图片
  • 获取任务:查看队首任务的下次执行是否达到,达到则返回任务,否则await等待任务执行时间达到;
    定时器的几种常用方案及原理_第5张图片
    优缺点: ScheduledThreadPoolExecutor为多线程模式,单个任务的耗时不会影响到其它任务,任务执行前和执行后都可扩展(可进行监控统计和异常处理等),很多框架都是以此来扩展更多的功能,目前只支持fixRate和fixDelay两种方式,cron表达式等不支持;

@Scheduled

  实现原理:

  • 生成定时任务:Spring在初始化bean后,通过postProcessAfterInitialization拦截所有标注@Scheduled注解的方法,解析注解参数转换成不同类型的定时任务,放入“定时任务列表”等待线程池进行处理;
  • 选择线程池:首先在Spring容器中寻找TaskScheduler的bean(按类型和名称taskScheduler),找不到则寻找ScheduledExecutorService的bean按类型和名称taskScheduler),仍找不到则使用默认的单线程taskScheduler;

将标注@Scheduled注解的方法转换成定时任务:
定时器的几种常用方案及原理_第6张图片
查找TaskScheduler生成的线程池:
定时器的几种常用方案及原理_第7张图片
查找ScheduledExecutorService生成的线程池:定时器的几种常用方案及原理_第8张图片
创建默认的线程池(单线程):
定时器的几种常用方案及原理_第9张图片
优缺点: 支持多种方式的定时任务:cron表达式、fixRate和fixDelay等,比ScheduledThreadPoolExecutor功能更全,而且属于Spring生态,使用更加方便,生产环境不建议使用;

Quartz

实现原理: Quartz为对等部署,每台机器都有一个单独的线程QuartzSchedulerThread从数据库的QRTZ_TRIGGERS表中取出最近N秒内将要执行的任务(任务会加锁防止被其它机器获取),然后按照任务的执行时间排序,获取队首的任务判断执行时间,如果已过期则提交到线程池进行处理,否则await等待,等任务执行完成后更新下次执行时间,并释放锁;
定时器的几种常用方案及原理_第10张图片
定时器的几种常用方案及原理_第11张图片
参考:

  1. springBoot中@Scheduled执行原理解析
  2. 阿里P8架构师谈:Quartz调度框架详解、运用场景、与集群部署实践
  3. Quartz集群原理及配置应用
  4. Springboot定时任务原理及如何动态创建定时任务

你可能感兴趣的:(Java基础,杂谈)