netty之HashedWheelTimer

本文介绍了如何使用Netty的HashedWheelTimer在Java开发中实现定时任务,特别是针对连续通知事件的场景,间隔2秒重复通知。HashedWheelTimer基于时间轮数据结构,通过一个环形的格子阵列和转动的指针来管理定时任务,其精度与格子数量和时间间隔有关。每个格子代表一定时间,任务通过取模分配到对应格子,指针转动到相应位置执行任务。

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一圈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非ban必选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值