完整实现-通过DelayQueue实现延时任务

一、DelayQueue的应用原理

二、订单延时任务的实现

三、订单处理

四、优缺点

一、DelayQueue的应用原理

DelayQueue是一个无界的BlockingQueue的实现类,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

BlockingQueue即阻塞队列,java提供的面向多线程安全的队列数据结构,当队列内元素数量为0的时候,试图从队列内获取元素的线程将被阻塞或者抛出异常。

这里的“无界”队列,是指队列的元素数量不存在上限,队列的容量会随着元素数量的增加而扩容。

DelayQueue实现了BlockingQueue接口,所以具有无界、阻塞的特点,除此之外它自己的核心特点就是:

「放入该队列的延时任务对象,只要到达延时时间之后才能被取到」。

DelayQueue不接收null元素

「DelayQueue只接受那些实现了接口的对象」

二、订单延时任务的实现

了解了DelayQueue的特点之后,我们就可以利用它来实现延时任务了,实现接口。

;;;;;/***延时订单任务*/publicclassOrderDelayObjectimplementsDelayed{privateStringname;privatelongdelayTime;//延时时间//实际业务中这里传订单信息对象,我这里只做demo,所以使用字符串了privateStringorder;publicOrderDelayObject(Stringname,longdelayTime,Stringorder){=name;//延时时间加上当前时间=()+delayTime;=order;}//获取延时任务的倒计时时间@OverridepubliclonggetDelay(TimeUnitunit){longdiff=();(diff,);}//延时任务队列,按照延时时间元素排序,实现Comparable接口@OverridepublicintcompareTo(@NotNullDelayedobj){(,((OrderDelayObject)obj).delayTime);}@OverridepublicStringtoString(){Datedate=newDate(delayTime);SimpleDateFormatsd=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");return"\nOrderDelayObject:{"+"name="+name+",time="+(date)+",order="+order+"}";}}

上文类中的order为订单信息对象,在实际的业务开发过程中应该是传递订单信息,用于取消订单业务的实现(订单30分钟不付款自动取消)。

Delayed接口继承自Comparable接口,所以需要实现compareTo方法,用于延时任务在队列中按照“延时时间”进行排序。

getDelay方法是Delayed接口方法,实现该方法提供获取延时任务的倒计时时间

三、订单处理

首先我们需要一个容器,永久保存延时任务队列,如果是Spring开发环境我们可以这样做。

@Bean("orderDelayQueue")publicDelayQueueOrderDelayObjectorderDelayQueue(){returnnewDelayQueueOrderDelayObject();}

当用户下单的时候,将订单下单任务放入延时队列

@ResourceprivateDelayQueueOrderDelayObjectorderDelayQueue;//发起订单下单的时候将订单演示对象放入(newOrderDelayObject("订单延时取消任务",30*60*1000,//延时30分钟"延时任务订单对象信息"));

系统内开启一个线程,不断的从队列中获取消息,获取到之后对延时消息进行处理。DelayQueue的take方法从队列中获取延时任务对象,如果队列元素数量为0,或者没有到达“延时时间的任务”,该线程会被阻塞。

@ComponentpublicclassDelayObjectConsumerimplementsInitializingBean{@ResourceprivateDelayQueueOrderDelayObjectorderDelayQueue;@OverridepublicvoidafterPropertiesSet()throwsException{while(true){OrderDelayObjecttask=();(());(());//根据order订单信息,去查询该订单的支付信息//如果用户没有进行支付,将订单从数据库中关闭//如果订单并发量比较大,这里可以考虑异步或线程池的方式进行处理}}}

需要说明的是,这里的while-true循环的延时任务处理是顺序执行的,在订单并发量比较大的时候,需要考虑异步处理的方式完成订单的关闭操作。我之前写过一个SpringBoot的可观测、易配置的线程池开源项目,可能会对你有帮助,源代码地址:

经过我的测试,放入orderDelayQueue的延时任务,在半小时之后得到正确的执行处理。说明我们的实现是正确的。

四、优缺点

使用DelayQueue实现延时任务非常简单,而且简便,全部都是标准的JDK代码实现,不用引入第三方依赖(不依赖redis实现、消息队列实现等),非常的轻量级。

它的缺点就是所有的操作都是基于应用内存的,一旦出现应用单点故障,可能会造成延时任务数据的丢失。如果订单并发量非常大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的风险。所以使用DelayQueue实现延时任务,只适用于任务量较小的情况。

版权声明:本站所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,不声明或保证其内容的正确性,如发现本站有涉嫌抄袭侵权/违法违规的内容。请举报,一经查实,本站将立刻删除。

相关推荐