当前位置: 首页 > news >正文

多层时间轮实现延迟消息

单层时间轮的弊端

对于单层时间轮来说,如果要拉长延迟的时间,要么增加时间轮中槽的数量,要么增大前进指针的时间间隔。但是这两种方式都有弊端

  1. 增加槽的数量:占用内存较高
  2. 增大前进指针的时间间隔:延迟时间不精确
    为了解决上面的问题,我们采用钟表中的秒针、分针、时针一样,通过多层时间轮来挂载延迟任务,这样就可以用少量的内存实现超长的延迟时间。

多层时间轮的结构

时间槽:每个时间轮包含若干时间槽,每个槽代表一个时间片段。
时间轮:多层时间轮通常包含多个时间轮,每一层的精度和范围不同。例如,第一层(细粒度)可以管理毫秒级任务,第二层(中等粒度)可以管理秒级任务,第三层(粗粒度)可以管理分钟级任务。
指针:每个时间轮都有一个指针,用于指向当前时间槽,随着时间的推移指针向前移动。
任务链表:每个时间槽中包含一个任务链表,用于存储定时任务。

代码实战

任务

public class TimerTask implements Runnable{private long delayTime;private Runnable task;public TimerTask(long delayTime, Runnable task) {this.delayTime = delayTime;this.task = task;}public long getDelayTime() {return delayTime;}public Runnable getTask() {return task;}@Overridepublic void run() {task.run();}
}

时间轮

public class TimeWheel {private int slots;private long interval;private int currentSlot = 0;private List<List<TimerTask>> wheel = new ArrayList<>();public TimeWheel(int slots, long interval) {this.slots = slots;this.interval = interval;for (int i = 0; i < slots; i++) {wheel.add(new ArrayList<>());}}public int getCurrentSlot() {return currentSlot;}/**** @param task*/public void addTask(TimerTask task) {//计算置放槽位long delayTime = task.getDelayTime() > interval * slots ? task.getDelayTime() - interval * slots : task.getDelayTime();int slot = (int) ((delayTime / interval) + currentSlot) % slots;//添加任务wheel.get(slot).add(task);}public List<TimerTask> advance() {//前进一个槽位currentSlot = (currentSlot + 1) % slots;//取任务List<TimerTask> tasks = new ArrayList<>(wheel.get(currentSlot));wheel.get(currentSlot).clear();return tasks;}
}

多层时间轮

public class MultiLevelTimeWheel {TimeWheel smallWheel = new TimeWheel(100, 100);//100个槽,每个槽间隔100msTimeWheel middleWheel = new TimeWheel(10, 10000);//10个槽,每个槽间隔10s(小时间轮的一个周期)TimeWheel largeWheel = new TimeWheel(10, 100000);//10个槽,每个槽间隔100s(中时间轮的一个周期)public void addTask(TimerTask task) {long delayTime = task.getDelayTime();if (delayTime < 10000) {smallWheel.addTask(task);} else if (delayTime < 100000) {middleWheel.addTask(task);} else if (delayTime < 1000000) {largeWheel.addTask(task);} else {System.out.println("不支持这么大的延迟");}}public void advance() {List<TimerTask> tasks = smallWheel.advance();for (TimerTask task : tasks) {task.run();}//小时间轮转一圈,需要让中时间轮前进一格,并将对应的任务下放至小时间轮的零刻度,待小时间轮再转一整圈时处理if (smallWheel.getCurrentSlot() == 0) {List<TimerTask> middleTasks = middleWheel.advance();for (TimerTask middleTask : middleTasks) {smallWheel.addTask(middleTask);}}//中时间轮转一圈,需要让大时间轮前进一格,并将对应的任务下放至中时间轮的零刻度if (smallWheel.getCurrentSlot() == 0 && middleWheel.getCurrentSlot() == 0) {List<TimerTask> largeTasks = largeWheel.advance();for (TimerTask largeTask : largeTasks) {middleWheel.addTask(largeTask);}}}
}

运行

public static void main(String[] args) {long start = System.currentTimeMillis();TimerTask timerTask1 = new TimerTask(3000, () -> System.out.println("延迟毫秒数:" + (System.currentTimeMillis() - start)));TimerTask timerTask2 = new TimerTask(33000, () -> System.out.println("延迟毫秒数:" + (System.currentTimeMillis() - start)));TimerTask timerTask3 = new TimerTask(333000, () -> System.out.println("延迟毫秒数:" + (System.currentTimeMillis() - start)));MultiLevelTimeWheel multiLevelTimeWheel = new MultiLevelTimeWheel();multiLevelTimeWheel.addTask(timerTask1);multiLevelTimeWheel.addTask(timerTask2);multiLevelTimeWheel.addTask(timerTask3);while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}multiLevelTimeWheel.advance();}}

这里的代码实现只是一个demo,主要是为了了解其中的思想。


http://www.mrgr.cn/news/36234.html

相关文章:

  • linux网络编程8
  • 使用Docker和Macvlan驱动程序模拟跨主机跨网段通信
  • 代理有什么用处?
  • 数据结构const char *INSTNAME[]
  • C++——输入一个字符串,把其中的字符按逆序输出。如输入LIGHT,输出THGIL。用string方法。
  • 个人文章汇总
  • 类与对象—python
  • 有威胁的武器武装检测系统源码分享
  • 前端接口报错302 [已解决]
  • 深入理解及如何使用main函数参数
  • MySQL高阶1965-丢失信息的雇员
  • seL4 Threads(四)
  • 快速排序(C语言实现)
  • 教你快速完成大模型 API 的调用
  • Windows内核编程基础(2)
  • ‌WPF Prism框架的优势主要体现
  • 每日OJ题_牛客_孩子们的游戏_约瑟夫环_C++_Java
  • 招联金融2025校招内推喇
  • 20_BERT微调训练
  • 【ASE】第三课_山丘颜色梯度效果