java訂單超時自動關閉

使用延時隊列DelayQueue實現訂單超時自動關閉
DelayQueue 是一個線程安全的隊列。可以實現異步操作

首先創建一個訂單實體類

@Getter
@Setter
public class OrderInfo implements Serializable , Delayed
{
    private static final long serialVersionUID = 1L;
    private String orderNo;// 訂單號
    private Integer status;// 訂單狀態
    private String expTime;// 訂單過期時間
    private String createTime;//訂單創建時間

    /**
     * 用於延時隊列內部比較排序:當前訂單的過期時間 與 隊列中對象的過期時間 比較
     */
    @Override
    public int compareTo(Delayed o) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long nowThreadtime = 0;
        long queueThreadtime = 0;
        try {
            nowThreadtime = formatter.parse(this.expTime).getTime();
            queueThreadtime = formatter.parse(((OrderInfo)o).expTime).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return Long.valueOf(nowThreadtime).compareTo(Long.valueOf(queueThreadtime));
    }

    /**
     * 時間單位:秒
     * 延遲關閉時間 = 過期時間 - 當前時間
     */
    @Override
    public long getDelay(TimeUnit unit) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long time = 0;
        try {
            time = formatter.parse(this.expTime).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return time - System.currentTimeMillis();
    }
}

下面實現隊列移除超市訂單

/**
 * [使用延時隊列DelayQueue實現訂單超時關閉]
 * [後臺守護線程不斷的執行檢測工作]
 * [雙檢查模式實現單例模式]
 */
@Component
public class OrderOverTimeClose
{
    private volatile static OrderOverTimeClose oderOverTimeClose = null;
	
	//我這裏是一個商品訂單實體類
    private GoodsOrderRepository goodsOrderRepository;

    private OrderOverTimeClose() {
        this.goodsOrderRepository = ApplicationContextProvider.getBean(GoodsOrderRepository.class);
    }
    /**
     * 單例模式,雙檢查鎖模式,在併發環境下對象只被初始化一次
     */
    public static OrderOverTimeClose getInstance(){
        if(oderOverTimeClose == null ){
            synchronized(OrderOverTimeClose.class){
                oderOverTimeClose =  new OrderOverTimeClose();
            }
        }
        return oderOverTimeClose;
    }

    /**
     * 守護線程
     */
    private Thread mainThread;

    /**
     * 啓動方法
     */
    public void init(){
        /**初始化時加載數據庫中需處理超時的訂單**/
        //循環訂單 獲取狀態 調用orderPutQueue();
        // 創建初始訂單
        long createTime = System.currentTimeMillis();
        String currentTime = OrderPay.getTime(createTime);
        String overTime = OrderPay.getTime(createTime + 1800000);// 三十分鐘後超時 10000毫秒=10秒
        //啓動的時候找到需要的訂單信息
        List<GoodsOrder> goodsOrders = goodsOrderRepository.findBy("status","1");
        //循環訂單信息 把需要的信息 放進隊列的實體類
        goodsOrders.forEach((info)->{
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderNo(info.getOrderNum());
            orderInfo.setStatus(info.getStatus());
            orderInfo.setCreateTime(currentTime);
            orderInfo.setExpTime(overTime);
            orderPutQueue(orderInfo,currentTime,overTime);
        });
        mainThread =  new Thread(() -> execute());
        mainThread.setDaemon(true);
        mainThread.setName("守護線程-->");
        mainThread.start();
    }

    /**
     * 創建空延時隊列
     */
    private DelayQueue<OrderInfo> queue = new DelayQueue<OrderInfo>();

    /**
     * 讀取延時隊列,關閉超時訂單
     */
    private void execute() {
        while (true) {
            try {
                if(queue.size() > 0){
                    //從隊列裏獲取超時的訂單
                    OrderInfo orderInfo = queue.take();
                    // 檢查訂單狀態,是否已經成功,成功則將訂單從隊列中刪除。
                    if (orderInfo.getStatus()>1) {
                        System.out.println("線程:"+Thread.currentThread().getName()+",訂單號:"
                                + orderInfo.getOrderNo() + ",訂單狀態:"
                                + orderInfo.getStatus() + ",訂單創建時間:"
                                + orderInfo.getCreateTime()
                                + ",訂單超時時間:" + orderInfo.getExpTime()+",當前時間:"+OrderPay.getTime(System.currentTimeMillis()));
                        orderRemoveQueue(orderInfo);
                        System.out.println("訂單已成功 移除掉"+orderInfo.getOrderNo());
                        Thread.sleep(2000);
                    } else {
						//這裏是超時的訂單 把超時的訂單狀態改爲取消                        
						GoodsOrderRepository goodsOrderRepository = ApplicationContextProvider.getBean(GoodsOrderRepository.class);
                        GoodsOrder goodsOrder = goodsOrderRepository.findOneBy("orderNum",orderInfo.getOrderNo());
                        if( goodsOrder!=null && goodsOrder.getStatus()<=1 )
                        {
                            goodsOrder.setStatus(GoodsOrder.orderType.cancelOrder);//取消訂單
                            goodsOrder.setCancelTime(new Date());
                            goodsOrderRepository.saveOrUpdate(goodsOrder);
                        }else
                        {
                            orderRemoveQueue(orderInfo);
                            System.out.println("訂單已成功 移除掉"+orderInfo.getOrderNo());
                        }
                        System.out.println("線程:"+Thread.currentThread().getName()+",訂單號:"
                                + orderInfo.getOrderNo() + ",變更訂單狀態爲:超時關閉"
                                + ",訂單創建時間:"
                                + orderInfo.getCreateTime()
                                + ",訂單超時時間:" + orderInfo.getExpTime()+",當前時間:"+OrderPay.getTime(System.currentTimeMillis()));
                        Thread.sleep(2000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 插入訂單到超時隊列中
     */
    public void orderPutQueue(OrderInfo orderInfo, String createTime,
                              String overTime) {
        System.out.println("訂單號:" + orderInfo.getOrderNo() + ",訂單創建時間:"
                + createTime + ",訂單過期時間:" + overTime);
        queue.add(orderInfo);
    }

    /**
     * 從延遲隊列中移除
     */
    public void orderRemoveQueue(OrderInfo orderInfo){
        if(null == orderInfo){
            return;
        }
        for (Iterator<OrderInfo> iterator = queue.iterator(); iterator.hasNext();) {
            OrderInfo info = (OrderInfo) iterator.next();
            if(orderInfo.getOrderNo().equals(info.getOrderNo())){
                queue.remove(info);
            }
        }
    }
}

創建一個測試類

/**
 * 模擬訂單支付,創建訂單
 */
public class OrderPay
{
    static Integer[] str = new Integer[]{1,2,3};

    public static String getTime(long time){
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date(time);
        String currentTime = formatter.format(date);
        return currentTime;
    }

    public static void main(String[] args) throws InterruptedException {
        OrderOverTimeClose.getInstance().init();

        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 2; i++) {
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    // 創建初始訂單
                    long createTime = System.currentTimeMillis();
                    String currentTime = getTime(createTime);
                    String overTime = getTime(createTime + 10000);// 十秒後超時
                    String orderNo = String.valueOf(new Random().nextLong());
                    OrderInfo order = new OrderInfo();
                    order.setOrderNo(orderNo);
                    order.setExpTime(overTime);
                    int random_index = (int) (Math.random()*str.length);
                    order.setStatus(str[random_index]);// 隨機分配
                    order.setCreateTime(currentTime);
                    OrderOverTimeClose.getInstance().orderPutQueue(order, currentTime, overTime);
                }
            };
            service.execute(run);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

好了 去試試吧 如果那裏錯了 或者不懂得 請指出來 Vx 號 AiMyHear

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章