利用J.U.C.ScheduledThreadPoolExecutor来实现订单状态的实时变更
来看一个场景:
一个叫车应用里,用户下单叫车,行程单初始状态为“行程中”,当行程结束后,状态自动变更为“待支付”。
叫车单有开始时间、结束时间。需求是要求当用户查看订单的时候,要及时体现出来最新的状态。就是说,如果是行程结束的订单,那么,展示的状态应该是“待支付”。 “行程中”状态的订单可以分享,“待支付”状态的订单可以发起支付。那么,程序怎么实现这个要求呢?
用定时任务?
不靠谱吧,间隔设置的再小也难免有延迟。况且,间隔设置太小,不停地操作数据库,会同时给应用服务器和数据库服务器带来压力。
如下几种方案:
【方案1】
最直观的实现方式,这也往往是大家想不到的方式。每个涉及到订单表的select操作,都先执行改状态的操作。更新状态的sql很简单,类似于:
update t_order set order_status= 'DaiZhiFu' where endTime≥now() and order_status='XingChengZhong'
加切面类是不错的实现方式。
只要用户操作或系统操作每次触发查询订单表,都执行这个update。显然,满足了需求。不过呢,弊端也显而易见,每次查单的时候并非都有需要变更状态的订单。如果查询频繁,因此而造成的系统负荷,可能得不偿失。
【方案2】中间件延迟消息队列
比如RabbitMQ,定义延迟消息队列。消息进入broker后,是先进入了延迟队列,当到了指定的ttl后,消息进入正常的消息队列进行消费。分布式系统适合用这种消息中间件的方式提高系统可用性。参考我的博客:利用延迟消息队列取代定时任务、 rabbitmq延迟队列demo
【方案3】
如题,利用juc下的ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor是java线程池的一员,jdk1.5之后提供的用来支持周期性任务的调度。ScheduledThreadPoolExecutor主要有两个成员,DelayedWorkQueue和ScheduledFutureTask。DelayedWorkQueue是一个延迟的阻塞队列(heap-based data structure,基于堆的数据结构),它会将入队的任务按照执行时间进行升序排序;ScheduledFutureTask,用来描述要执行的任务。通过调用delayedExecute方法来延时执行任务。 不说内部实现了,具体参考大神的blog:【JUC源码解析】ScheduledThreadPoolExecutor / 深入理解Java线程池:ScheduledThreadPoolExecutor。接下来直接看如何使用吧。
ThreadPoolBean.java定义ScheduledThreadPoolExecutor线程池对象:
package scheduledthreadpool; import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.extern.slf4j.Slf4j;import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; @Slf4j @Component public class ThreadPoolBean { @Bean public ScheduledThreadPoolExecutor bean() { ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("demo_pool_%d").build(); ScheduledThreadPoolExecutor poolExecutor = new ScheduledThreadPoolExecutor(5, namedThreadFactory); return poolExecutor; } }
OrderTask.java是修改订单状态的任务:
package scheduledthreadpool; @Slf4j public class OrderTask implements Runnable { private String orderNo; public OrderTask(String orderNo) { log.info("{} insert", orderNo); this.orderNo = orderNo; } @Override public void run() { log.info("{} updated", orderNo); } }
OrderController.java模拟用户下单叫车的api。
package scheduledthreadpool; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Slf4j @RestController public class OrderController { @Autowired private ScheduledThreadPoolExecutor orderStatusUpdatePool; @ResponseBody @GetMapping("/order/add") public String add(@RequestParam("sec") int sec) { String orderNo = System.currentTimeMillis()+"_"+sec; orderStatusUpdatePool.schedule(new OrderTask(orderNo), sec, TimeUnit.SECONDS); return "ok:" + orderNo; } }
MainApp.java是springboot启动程序。启动程序后,浏览器里访问http://localhost:8080/order/add?sec=**,多次改变sec参数的值,来模拟订单提交。从打印的log可以看出来,到了sec参数所指定的时间后,就会执行OrderTask的逻辑。
【The End】
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/13603732.html