DelayQueue简用

简介

DelayQueue
Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。例如,size方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。

DelayQueue类图结构

5879294-0c7d2460f594360e.png

在源码中看到 DelayQueue中内部使用的是PriorityQueue存放数据,使用ReentrantLock实现线程同步,可知是阻塞队列;

public class DelayQueue extends AbstractQueue
    implements BlockingQueue {

    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue q = new PriorityQueue();

元素实例

package com.cr.core.delay.entity;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 *
 * 订单超时实体类
 * Created by li on 2018/5/18.
 */
public class Order implements Delayed {

    /**
     * 订单号
     */
    private long orderId;
    /**
     * 开始执行时间
     */
    private long startTime;

    public Order(){

    }
    /**
     * orderId:订单id
     * timeout:订单超时时间,秒
     * */
    public Order(long orderId, int timeout){
        this.orderId = orderId;
        this.startTime = System.currentTimeMillis() + timeout*1000L;
    }
    /**
     * 返回当前对象的剩余延迟时间
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(startTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    /**
     * 比较当前对象与指定对象的顺序(出栈顺序)
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        if (o == this){
            return 0;
        }
        if(o instanceof Order){
            Order otherRequest = (Order)o;
            long otherStartTime = otherRequest.getStartTime();
            return (int)(this.startTime - otherStartTime);
        }
        return 0;
    }

    public long getOrderId() {
        return orderId;
    }

    public void setOrderId(long orderId) {
        this.orderId = orderId;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    @Override
    public String toString() {
        return "DSHOrder{" +
                "orderId=" + orderId +
                ", startTime=" + startTime +
                '}';
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }
}

应用实例

package com.cr.core.delay.service;

import com.cr.core.delay.entity.Order;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import java.util.concurrent.DelayQueue;

/**
 * Created by li on 2018/5/18.
 */
@Service
public class DelayService {

    private static final Logger log = Logger.getLogger(DelayService.class);


    private boolean start ;
    private DelayedListener listener;
    private DelayQueue delayQueue = new DelayQueue();
    //内部接口,监听器启动调本类start接口需要实现
    public interface DelayedListener{
        void delayedListener(Order order);
    }

    public void start(DelayedListener listener){
        if(start){
            return;
        }
        log.error("DelayService 启动");
        start = true;
        this.listener = listener;
        new Thread(()->{
            try{
                while(true){
                    Order order = delayQueue.take();
                    if(DelayService.this.listener != null){
                        DelayService.this.listener.delayedListener(order);
                    }
                }
            }catch(Exception e){
                log.info(e.getMessage(),e);
            }
        }).start();
    }

    public void add(Order order){
        delayQueue.put(order);
    }

    public boolean remove(Order order){
        return delayQueue.remove(order);
    }

    public void add(long orderId){
        delayQueue.put(new Order(orderId,24*3600*1000));
    }

    public void remove(long orderId){
        Order[] array = delayQueue.toArray(new Order[]{});
        if(array == null || array.length <= 0){
            return;
        }
        Order target = null;
        for(Order order : array){
            if(order.getOrderId() == orderId){
                target = order;
                break;
            }
        }
        if(target != null){
            delayQueue.remove(target);
        }
    }
}

监听器实例

package com.cr.core.delay.service;

import com.cr.core.delay.entity.Order;
import com.cr.core.delay.entity.ThreadPoolUtil;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

import java.util.List;

/**
 * Created by li on 2018/5/18.
 */
public class StartupListener implements ApplicationListener {
    private static final Logger log = Logger.getLogger(StartupListener.class);

    @Autowired
    DelayService delayService;
    @Override
    public void onApplicationEvent(ApplicationEvent evt) {

        if (evt.getSource() == null) {
            return;
        }
        //自动收货
        delayService.start((Order order)->{
            //异步来做
            ThreadPoolUtil.execute(()->{
                long orderId = order.getOrderId();
                //查库判断是否需要自动收货
                log.error("自动确认收货,onDelayedArrived():"+orderId);
                //从redis删除
                //redisService.delete(Constants.RedisKey.DSH_PREFIX+orderId, RedisService.DB.DSH);
                log.error("自动确认收货,删除redis:"+orderId);
            });
        });
        //查找需要入队的订单
        ThreadPoolUtil.execute(()->{
            log.error("查找需要入队的订单");
            //扫描redis,找到所有可能的orderId
            List keys = null;//redisService.scan(RedisService.DB.DSH);
            if(keys == null || keys.size() <= 0){
                return;
            }
            log.error("需要入队的订单keys:"+keys);
            //写到DelayQueue
            for(String key : keys){
                Order order = null;//redisService.get(key, DSHOrder.class, RedisService.DB.DSH);
                log.error("读redis,key:"+key);
                if(order != null){
                    //delayService.add(order);
                    log.error("订单自动入队:"+order.getOrderId());
                }
            }
        });
    }

}

使用场景

1、做订单超时、支付超时、和第三方交互时幂等重试等等;

你可能感兴趣的:(DelayQueue简用)