1、现有业务场景描述如下
当某一事件发生时,连续通知相关人员三次,第一次及时通知,以后两次的通知每次间隔2秒。
使用netty的定时器实现如下
2、netty的HashedWheelTimer
// 一个HashedWheelTimer是一个环形结构,可以想象成时钟,
// 分为很多格子,一个格子代表一段时间(越短Timer精度越高),
// 并用一个List保存在该格子上到期的所有任务,同时一个指针随着时间流逝一格一格转动,
// 并执行对应List中所有到期的任务。
// 这个类一般用来处理大量的定时任务且任务对时间精度要求相对不高,
// 比如链接超时管理等场景, 缺点是, 内存占用相对较高.该类中有两个重要的参数
// tickDuration:即一个格子代表的时间,默认为100ms
// ticksPerWheel (Wheel Size):一个Wheel含有多少个格子,默认为512个,
// 如果任务较多可以增大这个参数.
3、pom文件如下
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.75.Final</version>
</dependency>
4、代码实现如下
package com.example.demo.util;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class TimerLearn {
// 一个HashedWheelTimer是一个环形结构,可以想象成时钟,
// 分为很多格子,一个格子代表一段时间(越短Timer精度越高),
// 并用一个List保存在该格子上到期的所有任务,同时一个指针随着时间流逝一格一格转动,
// 并执行对应List中所有到期的任务。
// 这个类一般用来处理大量的定时任务且任务对时间精度要求相对不高,
// 比如链接超时管理等场景, 缺点是, 内存占用相对较高.该类中有两个重要的参数
// tickDuration:即一个格子代表的时间,默认为100ms
// ticksPerWheel (Wheel Size):一个Wheel含有多少个格子,默认为512个,
// 如果任务较多可以增大这个参数.
public static void main(String[] args) {
Timer timer = new HashedWheelTimer(100L, TimeUnit.MILLISECONDS, 512);
AtomicInteger atomicInteger = new AtomicInteger(0);
TimerLearn timerLearn = new TimerLearn();
//第一次执行任务不延时
// 第二次执行是在第一次执行完成后2秒后执行
// 第三次执行是在第二次执行完成后2秒后执行
// 任务执行3次后结束
TimerTask task = new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
//任务延时重新执行
timerLearn.reDoSomething(timer, this, atomicInteger);
}
};
timerLearn.doSomething(timer, task, atomicInteger);
while (true) {
int dotime = atomicInteger.get();
//获取任务执行的次数
if (dotime == 3) {
//当任务执行三次时 停止定时器 跳出循环
timer.stop();
break;
}
}
}
public void doSomething(Timer timer, TimerTask task, AtomicInteger atomicInteger) {
//第一次执行任务
Long time = System.currentTimeMillis() / 1000;
System.err.println(time + "===doSomething");
try {
//模拟任务耗时3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//任务执行次数加1
int doTimes = atomicInteger.addAndGet(1);
System.out.println(doTimes);
//第二次延时执行任务
timer.newTimeout(task, 2, TimeUnit.SECONDS);
}
public void reDoSomething(Timer timer, TimerTask task, AtomicInteger atomicInteger) {
if (atomicInteger.get() == 3) {
//执行次数 等于3的时候 不再执行
return;
}
Long time = System.currentTimeMillis() / 1000;
System.err.println(time + "===reDoSomething");
try {
//模拟任务耗时3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//任务执行次数加1
int doTimes = atomicInteger.addAndGet(1);
System.out.println(doTimes);
if (doTimes == 3) {
// 第三次执行不再添加到timer
return;
}
timer.newTimeout(task, 2, TimeUnit.SECONDS);
}
}
时间轮其实就是一种环形的数据结构,可以想象成时钟,分成很多格子,一个格子代码一段时间(这个时间越短,Timer的精度越高)。并用一个链表报错在该格子上的到期任务,同时一个指针随着时间一格一格转动,并执行相应格子中的到期任务。任务通过取摸决定放入那个格子。如下图所示:

假设一个格子是1秒,则整个wheel能表示的时间段为8s,假如当前指针指向2,此时需要调度一个3s后执行的任务,显然应该加入到(2+3=5)的方格中,指针再走3次就可以执行了;如果任务要在10s后执行,应该等指针走完一个round零2格再执行,因此应放入4,同时将round(1)保存到任务中。检查到期任务时应当只执行round为0的,格子上其他任务的round应减1。
HashedWheelTimer的构造方法:
- tickDuration
每一tick的时间 - timeUnit
tickDuration的时间单位 - ticksPerWheel
就是轮子一共有多个格子,即要多少个tick才能走完这个wheel一圈。
本文介绍了如何使用Netty的HashedWheelTimer在Java开发中实现定时任务,特别是针对连续通知事件的场景,间隔2秒重复通知。HashedWheelTimer基于时间轮数据结构,通过一个环形的格子阵列和转动的指针来管理定时任务,其精度与格子数量和时间间隔有关。每个格子代表一定时间,任务通过取模分配到对应格子,指针转动到相应位置执行任务。
1561

被折叠的 条评论
为什么被折叠?



