🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
一、什么是时间轮
时间轮这个技术其实出来很久了,在kafka、zookeeper等技术中都有时间轮使用的方式。 时间轮是一种高效利用线程资源进行批量化调度的一种调度模型。把大批量的调度任务全部绑定到同一个调度器上,使用这一个调度器来进行所有任务的管理、触发、以及运行。所以时间轮的模型能够高效管理各种延时任务、周期任务、通知任务。 以后大家在工作中遇到类似的功能,可以采用时间轮机制。如下图所示,时间轮,从图片上来看,就和手表的表圈是一样,所以称为时间轮,是因为它是以时间作为刻度组成的一个环形队列,这个环形队列采用数组来实现,数组的每个元素称为槽,每个槽可以放一个定时任务列表,叫HashedWheelBucket,它是一个双向链表,量表的每一项表示一个定时任务项(HashedWhellTimeout),其中封装了真正的定时任务TimerTask。时间轮是由多个时间格组成,下图中有8个时间格,每个时间格代表当前时间轮的基本时间跨度(tickDuration),其中时间轮的时间格的个数是固定的。在下图中,有8个时间格(槽),假设每个时间格的单位为1s,那么整个时间轮走完一圈需要8s钟。每秒钟指针会沿着顺时针方向移动一个,这个单位可以设置,比如以秒为单位,可以以一小时为单位,这个单位可以代表时间精度。通过指针移动,来获得每个时间格中的任务列表,然后遍历这一个时间格中的双向链表来执行任务,以此循环。
二、时间轮案例使用
这里使用的时间轮是Netty这个包中提供的,使用方法比较简单。
- 先构建一个HashedWheelTimer时间轮。
- tickDuration: 100 ,表示每个时间格代表当前时间轮的基本时间跨度,这里是100ms,也就是指针100ms跳动一次,每次跳动一个窗格
- ticksPerWheel:1024,表示时间轮上一共有多少个窗格,分配的窗格越多,占用内存空间就越大
- leakDetection:是否开启内存泄漏检测。
- maxPendingTimeouts[可选参数],最大允许等待的任务数,默认没有限制。
- 通过newTimeout()把需要延迟执行的任务添加到时间轮中
@RestController
@RequestMapping("/timer")
public class HashedWheelController {
//时间轮的定义
HashedWheelTimer hashedWheelTimer=new HashedWheelTimer(
new DefaultThreadFactory("demo-timer"),
100, TimeUnit.MILLISECONDS,1024,false);
@GetMapping("/{delay}")
public void tick(@PathVariable("delay")Long delay){
//SCHEDULED(定时执行的线程)
//Timer(Java原生定时任务执行)
System.out.println("CurrentTime:"+new Date());
hashedWheelTimer.newTimeout(timeout -> {
System.out.println("多少秒后执行这任务:"+new Date());
},delay,TimeUnit.SECONDS);
}
}
三、时间轮的原理解析
时间轮的整体原理,分为几个部分。
创建时间轮
时间轮本质上是一个环状数组,比如我们初始化时间轮时:ticksPerWheel=8,那么意味着这个环状数组的长度是8,如图3-12所示。
HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
添加任务
- 当通过newTimeout()方法添加一个延迟任务时,该任务首先会加入到一个阻塞队列中中。然后会有一个定时任务从该队列获取任务,添加到时间轮的指定位置,计算方法如下。
//当前任务的开始执行时间除以每个窗口的时间间隔,得到一个calculated值(表示需要经过多少tick,