1 使用场景
- 关闭空闲链接。服务器中,有不少客户端的链接,空闲一段时间以后须要关闭之。
- 清理过时数据业务上。好比缓存中的对象,超过了空闲时间,须要从缓存中移出。
- 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。
- 下单以后若是三十分钟以内没有付款就自动取消订单。
- 订餐通知:下单成功后60s以后给用户发送短信通知。
- 当订单一直处于未支付状态时,如何及时的关闭订单,并退还库存?
- 如何按期检查处于退款状态的订单是否已经退款成功?
- 新建立店铺,N天内没有上传商品,系统如何知道该信息,并发送激活短信?
- 定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行。
2 延时任务-实现方式
- 按期轮询(数据库等)
- DelayQueue
- Timer
- ScheduledExecutorService
- 时间轮(kafka)
- RabbitMQ
- Quartz
- Redis Zset
- Koala
- JCronTab
- SchedulerX(阿里)
- 有赞延迟队列
2.1 轮询
特色:按期轮训数据库,设置状态。java
优势:实现简单
缺点:数据量过大时会消耗太多的IO资源,效率过低
复制代码
2.2 DelayQueue
特色: 无界、延迟、阻塞队列面试
a、BlockingQueue+PriorityQueue(堆排序)+Delayed
b、DelayQueue中存放的对象须要实现compareTo()方法和getDelay()方法。
c、getDelay方法返回该元素距离失效还剩余的时间,当<=0时元素就失效了,
就能够从队列中获取到。
复制代码
这里为何要用leader/follower模式?数据库
- 若是不是队首节点,根本不须要唤醒操做!
- 假设取值时,延时时间尚未到,那么须要等待,但这个时候,队列中新加入了一个延时更短的,并放在了队首,那么 此时,for循环由开始了,取得是新加入的元素,那以前的等待就白等了,明显能够早点退出等待!
- 还有就是若是好多线程都在此等待,若是时间到了,同时好多线程会充等待队列进入锁池中,去竞争锁资源,但结果只能是一个成功, 多了写无畏的竞争!(屡次的等待和唤醒)
2.3 Timer与TimerTask
- TaskQueue中的排序是对TimerTask中的下一次执行时间进行堆排序,每次去取数组第一个。
- 而delayQueue是对queue中的元素的getDelay()结果进行排序
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它能够计划执行一个任务一次或反复屡次。 主要方法: 数组
2.4 时间轮(kafka)
时间轮名词解释:缓存
- 时间格:环形结构中用于存放延迟任务的区块;
- 指针(CurrentTime):指向当前操做的时间格,表明当前时间
- 格数(ticksPerWheel):为时间轮中时间格的个数
- 间隔(tickDuration):每一个时间格之间的间隔
- 总间隔(interval):当前时间轮总间隔,也就是等于ticksPerWheel*tickDuration
根据每一个TimerTaskEntry的过时时间和当前时间轮的时间,选择一个合适的bucket(实际上就是TimerTaskList),把这个TimerTaskEntry对象放进去,同时若是bucket的过时时间有更新,就将这个bucket推动DelayQueue,从新排序服务器
例子:假设编号为0的时间格或者桶保存着到期时间为t,每个tick的持续时间(tickDuration)为20ms,在这个格子里只能保存着到期时间为[t~t+20]ms的任务,假设时间轮的时间格有n个,每个间隔1ms,到期时间为m(ms),那么计算公式m%n = 所在的时间格或者桶,好比n=10,m=34ms,那么他所在桶或者时间格是4网络
2.5 RabbitMQ-延时任务
RabbitMQ自己没有直接支持延迟队列功能,可是能够经过如下特性模拟出延迟队列的功能。多线程
RabbitMQ能够针对Queue和Message设置 x-message-tt,来控制消息的生存时间,若是超时,则消息变为dead letter RabbitMQ针对队列中的消息过时时间有两种方法能够设置。 A: 经过队列属性设置,队列中全部消息都有相同的过时时间。 B: 对消息进行单独设置,每条消息TTL能够不一样。并发
2.6 Quartz
为何不用Timer?工具
- Timers没有持久化机制.
- Timers不灵活 (只能够设置开始时间和重复间隔,不是基于时间、日期、天等(秒、分、时)的)
- Timers 不能利用线程池,一个timer一个线程
- Timers没有真正的管理计划
核心概念:调度器、任务和触发器。
三者关系:调度器负责调度各个任务,到了某个时刻或者过了必定时间,触发器触动了,特定任务便启动执行。
- scheduler是一个计划调度器容器(总部),容器里面能够盛放众多的JobDetail和trigger,当容器启动后,里面的每一个JobDetail都会根据trigger循序渐进自动去执行。
- JobDetail是一个可执行的工做,它自己是有状态的。
- Trigger表明何时去调。
- 当JobDetail和Trigger在scheduler容器上注册后,造成了装配好的做业(JobDetail和Trigger所组成的一对儿),就能够伴随容器启动而调度执行了。
- scheduler是个容器,容器中有一个线程池,用来并行调度执行每一个做业,这样能够提升容器效率。
待续。。。
若是上面问题有什么疑问的话能够关注公众号,来和我一块儿讨论吧,关注便可免费领取海量最新java学习资料视频,以及最新面试资料。
若是你们以为这篇文章对你有帮助,或者你有什么疑问想提供1v1免费vip服务,均可以关注个人公众号,关注便可免费领取海量最新java学习资料视频,以及最新面试资料,你的关注和转发是对我最大的支持,O(∩_∩)O: