來看一個場景:
一個叫車應用里,用戶下單叫車,行程單初始狀態為“行程中”,當行程結束后,狀態自動變更為“待支付”,
叫車單有開始時間、結束時間,需求是要求當用戶查看訂單的時候,要及時體現出來最新的狀態,就是說,如果是行程結束的訂單,那么,展示的狀態應該是“待支付”, “行程中”狀態的訂單可以分享,“待支付”狀態的訂單可以發起支付,那么,程式怎么實作這個要求呢?
用定時任務?
不靠譜吧,間隔設定的再小也難免有延遲,況且,間隔設定太小,不停地操作資料庫,會同時給應用服務器和資料庫服務器帶來壓力,
兩種方案:
【方案1】
最直觀的實作方式,這也往往是大家想不到的方式,每個涉及到訂單表的select操作,都先執行改狀態的操作,更新狀態的sql很簡單,類似于:
update t_order set order_status= 'DaiZhiFu' where endTime≥now() and order_status='XingChengZhong'
加切面類是不錯的實作方式,
只要用戶操作或系統操作每次觸發查詢訂單表,都執行這個update,顯然,滿足了需求,不過呢,弊端也顯而易見,每次查單的時候并非都有需要變更狀態的訂單,如果查詢頻繁,因此而造成的系統負荷,可能得不償失,
【方案2】
如題,利用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】
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/13872.html
標籤:Java
