实现延时工作有很多的办法,网上对于延时工作的实现的文章曾经不少了。比方:实现延时工作的10种办法等等。然而这些文章基本上都是将办法大略的列举一下,给出局部示例代码,对于有教训的老程序员可能一看就晓得该怎么去把它实现残缺,然而对于初学者来说不够敌对。所以,我打算写一个系列的文章,具体的给出每种延时工作的实现办法、残缺实现代码,以及工作原理,欢送并期待大家关注我。
小概念:什么是延时工作?举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被主动勾销。订单30分钟不付款主动勾销,这个工作就是一个延时工作。
一、DelayQueue的利用原理
DelayQueue是一个无界的BlockingQueue的实现类,用于搁置实现了Delayed接口的对象,其中的对象只能在其到期时能力从队列中取走。
- BlockingQueue即阻塞队列,java提供的面向多线程平安的队列数据结构,当队列内元素数量为0的时候,试图从队列内获取元素的线程将被阻塞或者抛出异样。
- 这里的“无界”队列,是指队列的元素数量不存在下限,队列的容量会随着元素数量的减少而扩容。
DelayQueue实现了BlockingQueue接口,所以具备无界、阻塞的特点,除此之外它本人的外围特点就是:
- 放入该队列的延时工作对象,只有达到延时工夫之后能力被取到。
- DelayQueue 不接管null元素
- DelayQueue 只承受那些实现了java.util.concurrent.Delayed接口的对象
二、订单延时工作的实现
理解了DelayQueue的特点之后,咱们就能够利用它来实现延时工作了,实现java.util.concurrent.Delayed
接口。
import org.jetbrains.annotations.NotNull;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;/** * 延时订单工作 */public class OrderDelayObject implements Delayed { private String name; private long delayTime; //延时工夫 //理论业务中这里传订单信息对象,我这里只做demo,所以应用字符串了 private String order; public OrderDelayObject(String name, long delayTime, String order) { this.name = name; //延时工夫加上以后工夫 this.delayTime = System.currentTimeMillis() + delayTime; this.order = order; } //获取延时工作的倒计时工夫 @Override public long getDelay(TimeUnit unit) { long diff = delayTime - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } //延时工作队列,依照延时工夫元素排序,实现Comparable接口 @Override public int compareTo(@NotNull Delayed obj) { return Long.compare(this.delayTime, ((OrderDelayObject) obj).delayTime); } @Override public String toString() { Date date = new Date(delayTime); SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return "\nOrderDelayObject:{" + "name=" + name + ", time=" + sd.format(date) + ", order=" + order + "}"; }}
- 上文类中的order为订单信息对象,在理论的业务开发过程中应该是传递订单信息,用于勾销订单业务的实现(订单30分钟不付款主动勾销)。
- Delayed接口继承自 Comparable接口,所以须要实现compareTo办法,用于延时工作在队列中依照“延时工夫”进行排序。
- getDelay办法是Delayed接口办法,实现该办法提供获取延时工作的倒计时工夫
三、订单解决
首先咱们须要一个容器,永恒保留延时工作队列,如果是Spring开发环境咱们能够这样做。
@Bean("orderDelayQueue")public DelayQueue<OrderDelayObject> orderDelayQueue(){ return new DelayQueue<OrderDelayObject>();}
当用户下单的时候,将订单下单工作放入延时队列
@Resourceprivate DelayQueue<OrderDelayObject> orderDelayQueue;//发动订单下单的时候将订单演示对象放入orderDelayQueueorderDelayQueue.add( new OrderDelayObject( "订单延时勾销工作", 30 * 60 * 1000, //延时30分钟 "延时工作订单对象信息" ));
零碎内开启一个线程,一直的从队列中获取音讯,获取到之后对延时音讯进行解决。DelayQueue
的take办法从队列中获取延时工作对象,如果队列元素数量为0,或者没有达到“延时工夫的工作”,该线程会被阻塞。
@Componentpublic class DelayObjectConsumer implements InitializingBean { @Resource private DelayQueue<OrderDelayObject> orderDelayQueue; @Override public void afterPropertiesSet() throws Exception { while (true) { OrderDelayObject task = orderDelayQueue.take(); System.out.println(task.toString()); System.out.println(task.getOrder()); //依据order订单信息,去查问该订单的领取信息 //如果用户没有进行领取,将订单从数据库中敞开 //如果订单并发量比拟大,这里能够思考异步或线程池的形式进行解决 } }}
须要阐明的是,这里的while-true循环的延时工作解决时程序执行的,在订单并发量比拟大的时候,须要思考异步解决的形式实现订单的敞开操作。我之前写作一个SpringBoot的可观测、易配置的线程池开源我的项目,可能会对你有帮忙,源代码地址:https://gitee.com/hanxt/zimug...
通过我的测试,放入orderDelayQueue的延时工作,在半小时之后失去正确的执行解决。阐明咱们的实现是正确的。
四、优缺点
应用DelayQueue实现延时工作非常简单,而且简便,全部都是规范的JDK代码实现,不必引入第三方依赖(不依赖redis实现、音讯队列实现等),十分的轻量级。
它的毛病就是所有的操作都是基于利用内存的,一旦呈现利用单点故障,可能会造成延时工作数据的失落。如果订单并发量十分大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的危险。所以应用DelayQueue实现延时工作,只实用于任务量较小的状况。
欢送关注我的布告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com