时间轮算法
时间轮是一种高效、低延迟的调度数据结构。其在Linux内核中广泛使用,是Linux内核定时器的实现方法和基础之一。按使用场景,大致可以分为两种时间轮:原始时间轮和分层时间轮。分层时间轮是原始时间轮的升级版本,来应对时间“槽”数量比较大的情况,对内存和精度都有很高要求的情况。延迟任务的场景一般只需要用到原始时间轮就可以了。

代码案例
推荐使用Netty提供的HashedWheelTimer工具类来实现延迟任务。

引入依赖:

io.netty netty-common 4.1.23.Final 红包过期队列信息:

/**

  • 红包过期队列信息
    */
    public class RedPacketTimerTask implements TimerTask {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss.SSS”);

    /**

    • 红包 ID
      */
      private final long redPacketId;

    /**

    • 创建时间戳
      */
      private final long timestamp;

    public RedPacketTimerTask(long redPacketId) {
    this.redPacketId = redPacketId;
    this.timestamp = System.currentTimeMillis();
    }

    @Override
    public void run(Timeout timeout) {
    //异步处理任务
    System.out.println(String.format(“任务执行时间:%s,红包创建时间:%s,红包ID:%s”,
    LocalDateTime.now().format(F), LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).format(F), redPacketId));
    }
    }
    测试用例:

/**

  • 基于 netty 的时间轮算法 HashedWheelTimer 实现的延迟任务
    */
    public class RedPacketHashedWheelTimer {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss.SSS”);

    public static void main(String[] args) throws Exception {
    ThreadFactory factory = r -> {
    Thread thread = new Thread®;
    thread.setDaemon(true);
    thread.setName(“RedPacketHashedWheelTimerWorker”);
    return thread;
    };
    /**
    * @param tickDuration - 每tick一次的时间间隔
    * @param unit - tickDuration 的时间单位
    * @param ticksPerWheel - 时间轮中的槽数
    * @param leakDetection - 检查内存溢出
    */
    Timer timer = new HashedWheelTimer(factory, 1,
    TimeUnit.SECONDS, 100,true);
    System.out.println(String.format(“开始任务时间:%s”,LocalDateTime.now().format(F)));
    for(int i=1;i<10;i++){
    TimerTask timerTask = new RedPacketTimerTask(i);
    timer.newTimeout(timerTask, i, TimeUnit.SECONDS);
    }
    Thread.sleep(Integer.MAX_VALUE);
    }
    }
    打印任务执行日志:

开始任务时间:2020-02-12 15:22:23.404
任务执行时间:2020-02-12 15:22:25.410,红包创建时间:2020-02-12 15:22:23.409,红包ID:1
任务执行时间:2020-02-12 15:22:26.411,红包创建时间:2020-02-12 15:22:23.414,红包ID:2
任务执行时间:2020-02-12 15:22:27.424,红包创建时间:2020-02-12 15:22:23.414,红包ID:3
任务执行时间:2020-02-12 15:22:28.410,红包创建时间:2020-02-12 15:22:23.414,红包ID:4
任务执行时间:2020-02-12 15:22:29.411,红包创建时间:2020-02-12 15:22:23.414,红包ID:5
任务执行时间:2020-02-12 15:22:30.409,红包创建时间:2020-02-12 15:22:23.414,红包ID:6
任务执行时间:2020-02-12 15:22:31.411,红包创建时间:2020-02-12 15:22:23.414,红包ID:7
任务执行时间:2020-02-12 15:22:32.409,红包创建时间:2020-02-12 15:22:23.414,红包ID:8
任务执行时间:2020-02-12 15:22:33.411,红包创建时间:2020-02-12 15:22:23.414,红包ID:9
源码相关
其核心是workerThread线程,主要负责每过tickDuration时间就累加一次tick。同时也负责执行到期的timeout任务以及添加timeout任务到指定的wheel中。

构造方法:

public HashedWheelTimer(
ThreadFactory threadFactory,
long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
long maxPendingTimeouts) {

    if (threadFactory == null) {throw new NullPointerException("threadFactory");}if (unit == null) {throw new NullPointerException("unit");}if (tickDuration <= 0) {throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);}if (ticksPerWheel <= 0) {throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);}// Normalize ticksPerWheel to power of two and initialize the wheel.wheel = createWheel(ticksPerWheel);mask = wheel.length - 1;// Convert tickDuration to nanos.this.tickDuration = unit.toNanos(tickDuration);// Prevent overflow.if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {throw new IllegalArgumentException(String.format("tickDuration: %d (expected: 0 < tickDuration in nanos < %d",tickDuration, Long.MAX_VALUE / wheel.length));}//这里-爪洼笔记workerThread = threadFactory.newThread(worker);leak = leakDetection || !workerThread.isDaemon() ? leakDetector.track(this) : null;this.maxPendingTimeouts = maxPendingTimeouts;if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {reportTooManyInstances();}

}
新增任务,创建即启动:

public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
if (task == null) {
throw new NullPointerException(“task”);
}
if (unit == null) {
throw new NullPointerException(“unit”);
}

    long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {pendingTimeouts.decrementAndGet();throw new RejectedExecutionException("Number of pending timeouts ("+ pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "+ "timeouts (" + maxPendingTimeouts + ")");}//这里-爪洼笔记start();// Add the timeout to the timeout queue which will be processed on the next tick.// During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;// Guard against overflow.if (delay > 0 && deadline < 0) {deadline = Long.MAX_VALUE;}HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);timeouts.add(timeout);return timeout;

}
线程启动:

/**
* Starts the background thread explicitly. The background thread will
* start automatically on demand even if you did not call this method.
*
* @throws IllegalStateException if this timer has been
* {@linkplain #stop() stopped} already
*/
public void start() {
switch (WORKER_STATE_UPDATER.get(this)) {
case WORKER_STATE_INIT:
if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
workerThread.start();
}
break;
case WORKER_STATE_STARTED:
break;
case WORKER_STATE_SHUTDOWN:
throw new IllegalStateException(“cannot be started once stopped”);
default:
throw new Error(“Invalid WorkerState”);
}

    // Wait until the startTime is initialized by the worker.while (startTime == 0) {try {startTimeInitialized.await();} catch (InterruptedException ignore) {// Ignore - it will be ready very soon.}}
}

执行相关操作:

public void run() {
// Initialize the startTime.
startTime = System.nanoTime();
if (startTime == 0) {
// We use 0 as an indicator for the uninitialized value here, so make sure it’s not 0 when initialized.
startTime = 1;
}

        // Notify the other threads waiting for the initialization at start().startTimeInitialized.countDown();do {final long deadline = waitForNextTick();if (deadline > 0) {int idx = (int) (tick & mask);processCancelledTasks();HashedWheelBucket bucket =wheel[idx];transferTimeoutsToBuckets();bucket.expireTimeouts(deadline);tick++;}} while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);// Fill the unprocessedTimeouts so we can return them from stop() method.for (HashedWheelBucket bucket: wheel) {bucket.clearTimeouts(unprocessedTimeouts);}for (;;) {HashedWheelTimeout timeout = timeouts.poll();if (timeout == null) {break;}if (!timeout.isCancelled()) {unprocessedTimeouts.add(timeout);}}processCancelledTasks();

}
小结
以上方案并没有实现持久化和分布式,生产环境可根据实际业务需求选择使用。

源码
https://gitee.com/52itstyle/spring-boot-seckill
深圳网站建设www.sz886.com

推荐一款高效的处理延迟任务神器相关推荐

  1. 推荐一款开源的ICO制作神器——greenfish

    推荐一款开源的ICO制作神器--Greenfish Icon Editor Pro 关于ICO格式做软件图标时,为什么有时候图标太小,且不清晰,看这篇文章就会茅塞顿开. 制作一个标准 ICO 图标 - ...

  2. 为你推荐一款高效的IO组件——okio

    原文:为你推荐一款高效的IO组件--okio,点击链接查看更多技术内容. 前不久,三方组件库上新了一批JS/eTS组件,其中就包括okio组件.okio是一个可应用于HarmonyOS的高效IO库,它 ...

  3. 推荐三款 Mac 上的理财神器 iCompta、Rublik、UctoX

    推荐三款 Mac 上的理财神器 iCompta.Rublik.UctoX 今天推荐三款理财神器,像个人的话可以使用 iCompta(个人财务管理)一款软件就好了,但有些朋友可能有关注汇率的需求,可以使 ...

  4. 电脑软件:推荐10款实用的办公效率神器

    1.Everything 搜索神器 Everything超级文件搜索软件是速度最快的文件搜索软件,可以瞬间搜索到你需要的文件.Everything是速度最快的文件搜索软件,可以瞬间搜索到你需要的文件. ...

  5. 推荐几款高效的Python文本编辑器!

    我们都知道程序员花费大量的时间在编写.阅读和编辑代码上,因此一定要使用高效的文本编辑器才能够提高并很好的完成工作的效率和保证工作的质量. 什么是高效的文本编辑器呢?除了自己用的得心应手外,小U认为还应 ...

  6. 推荐一款多功能时间管理神器

    当我们需要待办事项提醒.备忘录.习惯打卡.日程管理等等的时候,往往需要下载不同的软件,非常的麻烦. 今天推荐一款跨平台,功能齐全(大部分免费),集备忘录.日历.习惯打卡.记账.提醒事项.日程规划等等众 ...

  7. 国庆假期,推荐一款假日必备的看片神器!

    点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要3分钟 Follow小博主,每天更新前沿干货 今天跟大家分享最近在 GitHub 比较火的一个开源软件:ZY-Player. GitHub:h ...

  8. 推荐一款接口自动化测试数据提取神器 JSonPath

    参考:https://cloud.tencent.com/developer/article/1838573?from=article.detail.1846104 在开发自定义校验规则库或者常规的接 ...

  9. 推荐一款牛逼的Windows神器!功能很强大!

    嗨喽,各位新来的小伙伴们,你们好!由于公众号做了改版,为了保证公众号的资源推送,能够第一时间及时送达,大家记得将搜罗哥的公众号 加星标置顶 ,在此真诚的感谢! 上篇推文: LeetCode1-50题汇 ...

  10. 知云文献翻译打不开_推荐一款阅读英文文献的神器,效率高不少,理解深不少!...

    这款软件用来阅读英文文献,思路非常顺畅.不卡壳,它自己就是一个pdf阅读器,又是一个翻译软件,随便选中一段话.一句话.一个单词,右侧就会给出翻译.再不用有道那样每次悬浮个窗口,屏幕上不该翻译的也到处翻 ...

最新文章

  1. 使用Categorical_endcoder包对标称变量进行个性化编码
  2. 20个方法让你摆脱坏习惯
  3. 【POJ - 3249】Test for Job(DAG线性求带负权的最长路,dp)
  4. 误删了公司数据库,但我还是活下来了
  5. android 广告栏效果,实现android广告栏效果
  6. 明星开餐厅十店九亏?明星靠“卖面子”能撑多久?
  7. 史上最强Dubbo面试26题和答案:核心组件+服务治理+架构设计等
  8. 【winfrom】Excel文件加载
  9. 计算机科学数学背景,计算机科学中的数学教育.pdf
  10. .NET in China - What's New in .NET
  11. 新用户域名实名认证教程---金万维
  12. 1062: 最大公约数 Python
  13. Feign原理:current list of Servers哪里来的
  14. 计算机语言有几进制,一个字节由几个二进制位组成(计算机系统有什么两部分组成)...
  15. 启动Storm的nimbus和supervisor时报错: ImportError: No module named argparse
  16. Audified U73b 复古压缩器评测:带来复古温暖的色彩
  17. VB与VB.NET的区别
  18. 蓝牙单火开关(天猫精灵生态)方案
  19. 2018------书籍电影和音乐
  20. win10能联网但右下角出现小地球,无Internet连接解决方法

热门文章

  1. AndroidQQ登录
  2. WindowsCMD配置代理
  3. 计算机辅助设计基础试题,CAD基础试题「附答案」
  4. 胡玉平 计算机科学,基于代价敏感混合分裂策略的多决策树算法
  5. 有关龙的成语(词语)、故事、诗歌
  6. 电脑硬盘:数据错误(循环冗余检查)
  7. unity 简易太空大战
  8. 气凝胶产业链都有?-供应银纳米粒子修饰碳球/石墨烯复合气凝胶/磷钨杂多酸修饰ZrO_2气凝胶/柠檬酸修饰纤维素气凝胶定制
  9. 关于LNK2005 _main 已经在 某某某.obj 中定义的问题
  10. Linux网络技术学习(二)—— net_device数据结构解析