每次推进都会更新 currentTime 为当前时间戳 , 当然做了点微调使得 currentTime 是 tickMs 的整数倍 。并且每次推进都会把能降级的任务重新插入降级 。
可以看到这里的 DelayQueue 的元素是每个槽 , 而不是任务 , 因此数量就少很多了 , 这应该是权衡了对于槽操作的延时队列的时间复杂度与空推进的影响 。
总结首先介绍了 Timer、DelayQueue 和 ScheduledThreadPool , 它们都是基于优先队列实现的 , O(logn) 的时间复杂度在任务数多的情况下频繁的入队出队对性能来说有损耗 。因此适合于任务数不多的情况 。
Timer 是单线程的会有阻塞的风险 , 并且对异常没有做处理 , 一个任务出错 Timer 就挂了 。而 ScheduledThreadPool 相比于 Timer 首先可以多线程来执行任务 , 并且线程池对异常做了处理 , 使得任务之间不会有影响 。
并且 Timer 和 ScheduledThreadPool 可以周期性执行任务 。而 DelayQueue 就是个具有优先级的阻塞队列 。
对比而言时间轮更适合任务数很大的延时场景 , 它的任务插入和删除时间复杂度都为O(1) 。对于延迟超过时间轮所能表示的范围有两种处理方式 , 一是通过增加一个字段-轮数 , Netty 就是这样实现的 。二是多层次时间轮 , Kakfa 是这样实现的 。
相比而言 Netty 的实现会有空推进的问题 , 而 Kafka 采用 DelayQueue 以槽为单位 , 利用空间换时间的思想解决了空推进的问题 。
可以看出延迟任务的实现都不是很精确的 , 并且或多或少都会有阻塞的情况 , 即使你异步执行 , 线程不够的情况下还是会阻塞 。
巨人的肩膀
推荐阅读
- 太阳系有第九颗行星吗 太阳系是否存在第九大行星
- 老茶头名称解读,老茶头有什么危害
- 空姐学茶艺推广茶文化,什么是茶文化
- 什么是Java可变参数列表?怎么和重载机制配合使用?
- 健康喝茶是浓是淡,怀孕了能喝茶吗
- 决明子茶有什么副作用,孕妇喝甘草胖大海的副作用是什么
- 喝什么茶淡斑,茶姿此生
- 喝什么茶消化好,什么茶老年人喝了好
- 喝茶水水的好处和坏处,用茶水敷脸有什么好处和坏处
- 上“中台”,是送分,还是送命?