时间轮-Java实现篇

article/2025/7/28 1:00:23

在前面的文章《时间轮-理论篇》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。

1. 理论抽象

将时间轮的理论进行抽象,主要有两个方面:

  • 时间轮的转动
  • 每一个时间间隔任务的处理,从时间间隔的Buket中取出要执行的任务,删除已经关闭的任务。将任务提交给线程池进行执行处理

2.Java实现

接口:

public interface Timer {void createTimerTask(TimerTask task, long delay, TimeUnit unit);}public interface TimerTask {void run();}

实现类:

public class TimeWheel implements Timer {private ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());private BlockingQueue<TimerTaskWrapper> addQueue = new ArrayBlockingQueue<>(128);//每一个tick时间间隔默认1毫秒private final long duration;//时间轮启动时间private volatile long startTime;private Thread timeWheelThread;private CountDownLatch startLatch = new CountDownLatch(1);//时间轮的tick数量private int tickNum = 128;private Bucket[] buckets;private boolean started = false;public TimeWheel(long duration, TimeUnit unit) {long nanos = unit.toNanos(duration);if (TimeUnit.MILLISECONDS.toNanos(1) > nanos) {this.duration = 1;} else {this.duration = unit.toMillis(duration);}this.timeWheelThread = new Thread(new Worker());this.buckets = new Bucket[tickNum];for (int i = 0; i < tickNum; ++i) {this.buckets[i] = new Bucket();}}@Overridepublic void createTimerTask(TimerTask task, long delay, TimeUnit unit) {start();long deadline = System.currentTimeMillis() + unit.toMillis(delay) - startTime;System.out.println("deadline="+deadline);addQueue.offer(new TimerTaskWrapper(task, deadline));}private void start() {if (started) {return;}started = true;timeWheelThread.start();try {startLatch.await();System.out.println("启动完成");} catch (Exception e) {e.printStackTrace();}}//处理时间轮的转动class Worker implements Runnable {//记录tick的次数private long tick;@Overridepublic void run() {startTime = System.currentTimeMillis();startLatch.countDown();System.out.println("Worker 启动完成..........");for (; ; ) {long time = nextTick();if (time <= 0) {continue;}int bucketIndex = (int) (tick & (tickNum - 1));System.out.println("bucketIndex="+bucketIndex);Bucket bucket = buckets[bucketIndex];//处理阻塞队列中的taskdoHandleQueneTask();//处理过期数据bucket.expire(time);tick++;}}private void doHandleQueneTask() {for (int i = 0; i < 1024; ++i) {TimerTaskWrapper taskWrapper = addQueue.poll();//队列为空if (taskWrapper == null) {break;}long taskTicks = taskWrapper.deadline / duration;taskWrapper.rounds = (taskTicks - tick) / tickNum;final long ticks = Math.max(taskTicks, tick);int bucketIndex = (int) (ticks & (tickNum - 1));System.out.println("inster bucketIndex = " + bucketIndex);Bucket bucket = buckets[bucketIndex];bucket.addTimerTask(taskWrapper);}}private long nextTick() {long deadline = duration * (tick + 1);for (; ; ) {long current = System.currentTimeMillis() - startTime;long sleepTime = deadline - current;if (sleepTime <= 0) {return current;}try {Thread.sleep(sleepTime);} catch (Exception e) {e.printStackTrace();}}}}class TimerTaskWrapper implements Runnable {private TimerTask task;//任务执行截止时间protected long deadline;//多少圈protected long rounds;TimerTaskWrapper prev;TimerTaskWrapper next;public TimerTaskWrapper(TimerTask task, long deadline) {this.task = task;this.deadline = deadline;}@Overridepublic void run() {task.run();}public void expire() {service.execute(this);}}class Bucket {TimerTaskWrapper head;TimerTaskWrapper tail;public void addTimerTask(TimerTaskWrapper task) {if (task == null) {return;}if (head == null) {tail = task;head = tail;} else {tail.next = task;task.prev = tail;tail = task;}}public TimerTaskWrapper removeTimerTask(TimerTaskWrapper task) {TimerTaskWrapper next = task.next;if (task.prev != null) {task.prev.next = next;}if (task.next != null) {task.next.prev = task.prev;}if (task == head) {if (task == tail) {head = null;tail = null;} else {head = next;}} else if (task == tail) {tail = task.prev;}task.prev = null;task.next = null;return next;}public void expire(long deadline) {TimerTaskWrapper task = head;while (task != null) {TimerTaskWrapper next = task.next;if (task.rounds <= 0) {next = removeTimerTask(task);if (task.deadline <= deadline) {task.expire();}} else {//减少时间轮的圈数task.rounds--;}task = next;}}}
}

编写一个测试案例来测试一下:

public class Test {public static void main(String[] args) {final TimeWheel wheel = new TimeWheel(1, TimeUnit.SECONDS);wheel.createTimerTask(new TimerTask() {@Overridepublic void run() {System.out.println(1111);wheel.createTimerTask(this, 4, TimeUnit.SECONDS);}}, 3,TimeUnit.SECONDS);}
}

运行打印结果:

在这里插入图片描述

说明:从日志的打印可以发现,在延迟三秒的情况下你会发现打印了 bucketIndex=xxx 四次。为什么会这样打印四次呢?因为当时间轮的tick在当前的时间间隔内,这个时间是不算的,从下个开始的。所以打印了四次。

3. 总结

从上面的实现可以看出来,时间间隔越长调用的就越不准确。例如刚开始的时候添加了任务到时间轮中,那么当前时间间隔就需要多消耗,实际的添加任务的执行时间为:当前时间轮剩下的时间+任务延迟执行的时间。所以如果对任务执行需要精确时间时间轮不适合。

我是蚂蚁背大象,文章对你有帮助点赞关注我,文章有不正确的地方请您斧正留言评论~谢谢!


http://chatgpt.dhexx.cn/article/bHfPKU2E.shtml

相关文章

时间轮timewheel算法

时间轮是个不太常见&#xff0c;但在部分场景有较高使用价值的工具。 时间轮常用于延时任务&#xff0c;在Netty、akka、Quartz、Zookeeper等高性能组件中都存在时间轮定时器的踪影。 从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的…

定时器实现原理——时间轮

时间轮 时间轮算法是通过一个时间轮去维护定时任务&#xff0c;按照一定的时间单位对时间轮进行划分刻度。然后根据任务延时计算任务落在该时间轮的第几个刻度上&#xff0c;如果任务时长超出了刻度数量&#xff0c;则需要增加一个参数记录时间轮需要转动的圈数。 简单时间轮…

时间轮算法

一、时间轮算法 1. 时间轮的基本概念 Kafka中存在大量的延时操作&#xff0c;如延时生产、延时消费等&#xff1b;而JDK中自带的 Timer 和 DelayQueue 的插入和删除操作的平均复杂度为 O&#xff08;nlogn&#xff09;&#xff0c;无法满足 Kafka 的高性能要求&#xff0c;因…

时间轮算法(TimingWheel)

时间轮算法的应用非常广泛&#xff0c;在 Dubbo、Netty、Kafka、ZooKeeper、Quartz 的组件中都有时间轮思想的应用&#xff0c;甚至在 Linux 内核中都有用到。 其思想理论基础可以参考论文&#xff1a;http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pd…

那些惊艳的算法们(三)—— 时间轮

从定时任务说起 自然界中定时任务无处不在&#xff0c;太阳每天东升西落&#xff0c;候鸟的迁徙&#xff0c;树木的年轮&#xff0c;人们每天按时上班&#xff0c;每个月按时发工资、交房租&#xff0c;四季轮换&#xff0c;潮涨潮落&#xff0c;等等&#xff0c;从某种意义上…

1、时间轮

一、什么是时间轮&#xff1f; 作为一个粗人&#xff0c;咱不扯什么高级的词汇&#xff0c;直接上图&#xff1a; 上面是一张时间轮的示意图&#xff0c;可以看到&#xff0c;这个时间轮就像一个钟表一样&#xff0c;它有刻度&#xff0c;图中画了9个格子&#xff0c;每个格子…

浅谈时间轮算法

浅谈时间轮算法 基于队列的定时任务执行模型缺陷 在计算机世界中&#xff0c;只有待解决的问题变得大规模后&#xff0c;算法的价值才能够最大化的体现。时间轮算法可以将插入和删除操作的时间复杂度都降为 O(1)&#xff0c;在大规模问题下还能够达到非常好的运行效果。 如果…

时间轮(TimingWheel)算法总结

通过阅读篇文章您可以很容易理解平时所使用的开源框架是如何进行任务调度的。而且对于以后业务上碰到需要做时间任务调度的需求&#xff0c;也可以尝试着用实践论算法去实现。 一、时间轮的应用 其实早在1987年&#xff0c;时间轮算法的论文已经发布。论文名称&#xff1a;Hash…

时间轮 (史上最全)

缓存之王 Caffeine 中&#xff0c;涉及到100w级、1000W级、甚至亿级元素的过期问题&#xff0c;如何进行高性能的定时调度&#xff0c;是一个难题。 注&#xff1a; 本文从 对 海量调度任务场景中&#xff0c; 高性能的时间轮算法&#xff0c; 做了一个 系统化、由浅入深的 穿透…

时间轮(Timing Wheel)案例和原理

什么是时间轮(Timing Wheel) 时间轮(Timing Wheel)是George Varghese和Tony Lauck在1996年的论文Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility实现的&#xff0c;它在Linux内核中使用广泛&#xff0c;是Linux内核定时器…

时间轮(TimingWheel)

一、什么是时间轮 时间轮其实就是一种环形的数据结构&#xff0c;可以想象成时钟&#xff0c;分成很多格子&#xff0c;一个格子代表一段时间&#xff08;这个时间越短&#xff0c;Timer的精度越高&#xff09;。并用一个双向链表存储放在该格子上的延时任务&#xff0c;同时一…

高性能定时器3——时间轮

​ 在网络程序中我们通常要处理三种事件&#xff0c;网络I/O事件、信号以及定时事件&#xff0c;我们可以使用I/O复用系统调用&#xff08;select、poll、epoll&#xff09;将这三类事件进行统一处理。我们通常使用定时器来检测一个客户端的活动状态&#xff0c;服务器程序通常…

C#,斐波那契数列(Fibonacci Sequence)的八种算法与源代码

一、莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09; 斐波那契公元1170年生于意大利比萨&#xff0c;卒于1250年&#xff0c;被人称作“比萨的莱昂纳多”&#xff0c;是一名闻名于欧洲的数学家&#xff0c;其主要的著作有《算盘书》、《实用几何》和《四艺经》等。…

matlab斐波那契数列画图,斐波拉契数列 斐波那契数列 matlab程序

斐波那契数列数列从第3项开始,每一项都等于前两项之和。 例子:数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368 应用: 生活斐波那契 斐波那契数列中的斐波脾气有点大,表面温和到别人误以为很好欺…

快速计算斐波那契数列(Fibonacci数列)

本文最后更新于 619 天前&#xff0c;其中的信息可能已经有所发展或是发生改变。 题目描述 输入一个正整数n&#xff0c;求Fibonacci数列的第n个数。Fibonacci数列的特点&#xff1a;第1,2个数为1,1。从第3个数开始&#xff0c;概述是前面两个数之和。即&#xff1a; 要求输入的…

递归求斐波那契数列

斐波那契数列 题目描述&#xff1a;编写一个函数&#xff0c;求斐波那契数列的第n项的值。 首先&#xff0c;对于斐波那契数列&#xff0c;我们是非常熟悉了&#xff0c;对斐波那契定义为如下&#xff1a;f(0)0,f(1)0,f(2)1,……f(n)f(n-1)f(n-2)&#xff0c;其中n>1。对于这…

斐波那契数列(Fibonacci)

有一对兔子&#xff0c;出生后第3个月起每个月都生一对免子。小兔子长到第3个月后每个月又生一对兔子。假设所有兔子都不死&#xff0c;问40个月的免子总数为多少?解题思路&#xff1a; 这是一个有趣的古典数学问题。可以从表1看出兔子繁殖的规律。 …

【递归 动态规划 备忘录法】Fibonacci数列(斐波那契数列)(C++)

一、什么是Fibonacci数列 斐波那契数列指的是这样一个数列&#xff1a;1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144…用文字来说&#xff0c;就是从第3项开始&#xff0c;每一项都等于前两项之和。至于包不包括0&#xff0c;一番查阅后没有得到证实… 不过重要的是“第3项开始…

算法-斐波那契数列Fibonacci

斐波那契数列 Fibonacci 斐波那契数列是这样的数列: 0、1、1、2、3、5, 8、13、21、34 …… 下一项是上两项的和。 2 是上两项的和(1+1) 3 是上两项的和(1+2)、 5 是(2+3)、 依此类推! 更多有意思的介绍可以见参考链接; 算法 1. 直接递归 初步想法就是采用递归的…

回顾斐波那契数列

一 概述 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列。该数列是指递归方法定义为&#xff1a; F(0) 0&#xff0c;F(0) 1&#xff0c;F(n) F(n-1) F(n-2) (n ≥ 2&#xff0c;n ∈ N*) 的数列。 具体表现形式为&#xff1a;0&am…