时间轮算法

思考

假如现在有个任务需要3s后执行,你会如何实现?

线程实现:让线程休眠3s

如果存在大量任务时,每个任务都需要一个单独的线程,那这个方案的消耗是极其巨大的,那么如何实现高效的调度呢?

时间轮算法就被提出来了

时间轮实现:下图是一个有12个时间格的时间轮,转完一圈需要12s。当我们需要新建一个3s后执行的定时任务,只需要将定时任务放在下标为3的时间格中即可。

当我们需要创建一个12s后执行的定时任务呢?(当前槽位为3)

添加刻度实现,按1秒为一个时间刻度,那么一天会有86400个刻度,当我继续添加一个任务,是86000秒后执行,那么其中大部分轮询都是空轮询,而且会浪费内存空间(每个刻度都有自己的任务队列)

这个时候可以引入“圈数/轮数”的概念,也就是说这个任务还是放在

下标为3的时间格中,不过它的圈数为1(圈数从0开始)。

当我们需要创建一个350s后自动完成的定时任务呢?

如果都用 round 来处理的话,那这个 round 将会变的非常大的一个数字,也会在任务列表中插入很多当前不需要执行的任务,如果每次都执行上面的逻辑,显然会浪费大量的资源。

时间轮

时间轮的数据结构

如上图所示,就是时间轮的一个基础结构,一个 存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表(TimerTaskList)。定时任务列表 是一个环形的双向链表,链表中的每一项表示的都是定时任务项(TimerTaskEntry),其中封装了真正的定时任务 TimerTask。

单层时间轮

现在我们不增加刻度,而是通过添加round属性来描述任务。假设我们时间大小设置为20s,我们添加三个任务:

  1. 10秒后执行的任务
  2. 30秒后执行的任务
  3. 50秒后执行的任务

那么这三个任务都在10刻度的任务队列中,分别为round=0, round=1, round=2。时间轮每移动一个刻度时,遍历任务队列取出round=0的任务执行,然后将其余的任务 round-1。

槽位计算公式:定时器放置的槽位 =(当前时间 + 定时器的延迟时间)%  槽位数量

(3+12)%12=3

10%20=10

30%20=10

50%20=10

任务执行轮次的计算公式: 定时器执行圈数 = ((任务执行时间 - 当前时间)/ 固定单位时间)% 槽位数量

((12-0)/12)%12 = 0

((10-0)/20)%20 = 0

((30-0)/20)%20 = 1

((50-0)/20)%20 = 2

基于round的时间轮虽然解决了浪费内存空间的问题,但当时间刻度小,任务队列长的时候会增加耗时。

多层时间轮

与基于round的时间轮不同,分层时间轮采用层级联动的方式,具有以下特点:

  1. 不做遍历计算round,每一个刻度中的任务都是应该执行的。
  2. 当任务执行时间超过当前刻度范围时,进入下一层时间轮的范围。
  3. 定时任务通过升级和降级来转移队列中的位置。

基本模型构成

  • tickMs(基本时间跨度):时间轮由多个时间格组成,每个时间格代表当前时间轮的基本时间跨度(tickMs)。
  • wheelSize(时间单位个数):时间轮的时间格个数是固定的,可用(wheelSize)来表示,那么整个时间轮的总体时间跨度(interval)可以通过公式 tickMs × wheelSize计算得出

上图的时间轮,设第 1 层的时间精度为 1s,第 2 层的时间精度为 20s,第 3 层的时间精度为 400s。假如我们需要添加一个 350s 后执行的任务 A 的话(当前时间是 0s),这个任务会被放在第 2 层(因为第二层的时间跨度为 20*20=400>350)的第 350/20=17 个时间格子。

当第一层转了 17 圈之后,时间过去了 340s ,第 2 层的指针此时来到第 17 个时间格子。此时,第 2 层第 17 个格子的任务会被降级到第 1 层。

任务 A 当前是 10s 之后执行,因此它会被移动到第 1 层的第 10 个时间格子。

时间轮的应用

时间轮的思想应用范围非常广泛,各种操作系统的定时任务调度,Crontab、Dubbo、新版的XXL-JOB、还有基于java的通信框架Netty中也有时间轮的实现,几乎所有的时间任务调度系统采用的都是时间轮的思想。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/115826.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为云云耀云服务器 L 实例评测|配置教程 + 用 Python 简单绘图

文章目录 Part.I IntroductionChap.I 云耀云服务器 L 实例简介Chap.II 参与活动步骤 Part.II 配置Chap.I 初步配置Chap.II 配置安全组 Part.III 简单使用Chap.I VScode 远程连接华为云Chap.II 简单绘图 Reference Part.I Introduction 本篇博文是为了参与华为“【有奖征文】华…

Linux常用命令—find命令大全

文章目录 一、find命令常用功能1、find命令的基本信息如下。2、按照文件名搜索3、按照文件大小搜索4、按照修改时间搜索5、按照权限搜索举例:6、按照所有者和所属组搜索7、按照文件类型搜索8、逻辑运算符 一、find命令常用功能 1、find命令的基本信息如下。 命令名…

招商信诺人寿基于 Apache Doris 统一 OLAP 技术栈实践

本文导读: 当前,大数据、人工智能、云计算等技术应用正在推动保险科技发展,加速保险行业数字化进程。在这一背景下,招商信诺不断探索如何将多元数据融合扩充,以赋能代理人掌握更加详实的用户线索,并将智能…

七分钟,数据转换器get到了

全文阅读时间 | 预计七分钟 KING BASE 开源 OR 闭源? 在瞬息多变的软件市场上,开源还是闭源是一个恒久不变的话题。开源软件得益于基础架构和基本功能的全面开放,开发者能自由使用和二次开发,但使用前提是需要投入大量成本对软件进…

基于Vue+ELement搭建登陆注册页面实现后端交互

🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《ELement》。🎯🎯 &#x1…

【观察】华为陈帮华:学习“都江堰模式”,构筑强健分销体系

目前在水利生态构建上,最有特点无疑是都江堰工程,它是以无坝引水为特征的水利生态工程,从无坝引水到平原灌溉,均有口无闸,既充分利用了河道的弯曲和崖壁的角度,自然控制流向和水量,同时在不改变…

【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)

食用指南:本文在有C基础的情况下食用更佳 🔥这就不得不推荐此专栏了:C语言 ♈️今日夜电波:靴の花火—ヨルシカ 0:28━━━━━━️💟──────── 5:03 …

自动化测试的定位及一些思考

大家对自动化的理解,首先是想到Web UI自动化,这就为什么我一说自动化,公司一般就会有很多人反对,因为自动化的成本实在太高了,其实自动化是分为三个层面的(UI层自动化、接口自动化、单元测试)&a…

电巢科技出席第26届西北地区电子技术与线路课程教学改革研讨会,聚焦一流课程建设!

2023年9月15日至17日,北方民族大学召开第26届西北地区电子技术与线路课程教学改革研讨会。本次会议围绕“梳理课程教学内容,改革教学方式,探索虚拟教研室构建方式,完善基层教学组织,推进一流课程和一流教材资源共享&am…

Java 基于微信小程序的学生选课系统

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 第一章: 简介第二章 技术栈第三章: 功能分析第四章 系统设计第五章 系统功…

java:java.util.MissingResourceException: Cant find bundle for base name解决方式

java:java.util.MissingResourceException: Cant find bundle for base name解决方式 1 前言 代码执行如下: ResourceBundle.getBundle("res.Message",Locale.getDefault(), ReadMyProps.class.getClassLoader());或 ResourceBundle.getBu…

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1: int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址,&a就表示整个数组,因此…