java 中高并发(DelayQueue)DelayQueue延时队列操作实例(2018.7.16)

应用场景 :

               用户购票后,产生未支付订单,系统30分钟后未支付自动取消。(每个订单放入线程中设置超时时间,自动处理)

                  本文 用一个线程 来循环跑对列,取消队列中的数据!!!

业务实现思路:

                用户下单时,把当前订单放入队列中 设置过期时间,

知识回顾:

               队列(Queue):(https://blog.csdn.net/zhongguozhichuang/article/details/53196415)

               BlockingQueue(组塞线程):https://www.cnblogs.com/KingIceMou/p/8075343.html

               ExecutorService(线程池):点击打开链接

              本文对线程的基本概念也需要理解!


// =========================================准备工作

DelayQueue: 准备前缀 创建队列对象 点击打开链接   (https://blog.csdn.net/u012859681/article/details/77836139)

/**

*  实现 Delayed 类 重写 方法

*/

public class OrderItem implements Delayed {


    private final long delayTime; // 延迟时间
    private final long expire; // 到期时间
    private Object data; // 数据

    public OrderItem(){
        delayTime=0;
        expire=0;
    }
    public OrderItem(long delay, Object data) {
        delayTime = delay;
        this.data = data;
        expire = System.currentTimeMillis() + delay;
    }

    /**
     * 优先队列里面优先级规则  TimeUnit .MILLISECONDS 获取单位 为毫秒的时间戳
     */
    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    /**
     * 剩余时间=到期时间-当前时间  convert: 将给定单元的时间段转换到此单元。
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("DelayedElement{");
        sb.append("delay=").append(delayTime);
        sb.append(", expire=").append(expire);
        sb.append(", data='").append(data.toString()).append('\'');
        sb.append('}');
        return sb.toString();
    }
    public Object getData() {
        return data;
    }
    
    

}


// ======================================================util类取消下单

first One: 创建队列util类

// 队列线程

public class ItemQueueThread {

   public static DelayQueue delayQueue = new DelayQueue();

    public static Thread thread;
    
    /**
     * flag=0:当前线程进入等待,flag=1当前线程可以竞争cpu使用权
     */
    public static String flag="1";

    public static void cancelOrder() {
        thread = new Thread(new Runnable() {
            @SuppressWarnings("null")
            public void run() {
                Thread.currentThread().setName("30分钟未支付自动取消run");

                try {
                    synchronized (delayQueue) {
                        if (delayQueue.size() <= 0) {
                            logger.info("当前线程进入等待");
                            flag="0"; // 等待
                            delayQueue.wait(5*60*1000);
                        }else{
                            TicketOrderService orderService = (TicketOrderService) SpringContextUtil
                                    .getBean(TicketOrderService.class);
                            Map map = Thread.getAllStackTraces();
                            logger.info("线程数--------" + map.size() + "延迟队列中数据元素:" + delayQueue.size());
                            OrderItem item = null;
                            try {
                                item = delayQueue.take(); // 队列取值。没有的时候等待
                            } catch (InterruptedException e) {
                                TicketOrder order = (TicketOrder) item.getData();
                                logger.error("定时取消订单失败,异常:" + e + "\n订单id" + order.getOrderId());
                            }
                            if (item != null) {
                                TicketOrder ticketOrder = (TicketOrder) item.getData();
                                ticketOrder.setOrderId(ticketOrder.getOrderId());
                                ticketOrder.setOrderStatus(OrderStatusEnum.zero.getValue());
                                ticketOrder.setCancelNumber(6);
                                ticketOrder.setCancelReason("支付超时");
                                String result = orderService.update(ticketOrder, null);
                                logger.info("      ticketOrder" + JSON.toJSONString(ticketOrder)+"自动取消结果:"+result);
                                try {
                                    cancelOrder(item);// 取消订单 方法,(此方法为业务方法不予展示)
                                } catch (IOException | TemplateException | JSONException e) {
                                    logger.error("定时取消订单失败,异常:" + e);
                                }
                                logger.info("过期时间:" + item.toString());
                            }
                        }
                        run(); // 循环调用一个线程跑队列
                    }

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }

/**
     * 下单成功的情况下取消订单,唤醒取消订单线程
     */
    public static void threadNotify() {
        if("0".equals(flag)){
            synchronized (delayQueue) {
                if("0".equals(flag)){
                    flag="1";
                    delayQueue.notify();
                }
            }
        }
    }





// ===============================start下单放入线程=============


start:      用户 进入 购票页面 购票 放入线程监控

public Map insert(HttpServletRequest request,HttpServletResponse response,Model model,TicketOrder ticketOrder) {
   

DelayQueue items=ItemQueueThread.delayQueue;

            Map map1 = Thread.getAllStackTraces();
            logger.info("线程数--------------" + map1.size());
            
           // map=orderService.insert(ticketOrder, token); 录入订单
            int num=(int) map.get("result");
            
            if(num>0){
                items.offer(new OrderItem(30*60*1000,ticketOrder));
                ItemQueueThread.threadNotify();
                map.put("status", "000");//下单成功
                
                //下单成功,将成功订单信息存入缓存,设置过期时间30*60 秒  放入redis 缓存 id和时间  这里不做介绍
                myRedisService.set(ticketOrder.getOrderId(), JSON.toJSONString(ticketOrder));
                myRedisService.expire(ticketOrder.getOrderId(), 30*60);
            }else{
                 items.offer(new OrderItem(1*1000,ticketOrder));
                 ItemQueueThread.threadNotify(); 
                 map.put("status", "001");//下单失败 ,直接取消订单
            }
        }else{
            if("14001".equals(resultMap.get("code"))){
                map.put("status", "004");//身份证号码不正确
            }else{
                map.put("status","001");//下单失败
            }
        }
        return map;
    }

// 项目启动时查询所有未支付订单 放入队列中 ,初始化队列

public class InitData implements ApplicationRunner {
	
	@Autowired
	private MyRedisService myRedisService;
	
	@Autowired
	private ParameterInfoService parameterService;
	@Autowired	
	private TicketOrderService ticketOrderService;
	
	@SuppressWarnings("unchecked")
	@Override
	public void run(ApplicationArguments args) throws Exception {
		
		//获取所有超时未支付订单
		DelayQueue items=	ItemQueueThread.delayQueue;
		List orders=ticketOrderService.selectTimeoutUnpaid(null);
		if(orders!=null && orders.size()>0){
			for (int i = 0; i < orders.size(); i++) {
				int jsonInfo=Integer.parseInt(orders.get(i).getJsonInfo());		
				if(jsonInfo>0){
					items.offer(new OrderItem(jsonInfo*1000,orders.get(i)));
	    			myRedisService.set(orders.get(i).getOrderId(), JSON.toJSONString(orders.get(i)));
	    			myRedisService.expire(orders.get(i).getOrderId(), jsonInfo);
				}else{
					items.offer(new OrderItem(i*1000,orders.get(i)));
				}
			}
		}
		ItemQueueThread.cancelOrder();
	}

}


 

               


你可能感兴趣的:(java)