DelayQueue使用

DelayQueue是一个延迟队列。在指定时间才能获取队列元素,队列头元素是即将过期的元素。

元素需要实现  Delayed 接口。该接口有两个抽象方法,

    getDelay(TimeUnit.NANOSECONDS)返回元素延迟时长.DelayQueue在取出元素后,会判断该方法返回是否<0,是才能取出该元素。

    compareTo。返回元素在队列中的排序方式,一般根据getDelay来计算

DelayQueue实现了BlockingQueue,是一个阻塞队列。调用take时,如果没有找到元素,当前线程会被阻塞,等待队列有元素时会被唤醒。

业务场景: 用户下订单,到指定时间不支付处理订单过期

下面给出思路和主要代码

1. 定义一个订单延时过期的主体。省去getter setter


/**
 * 延时信息
 */
public class MyDelayMessage implements Delayed{

    /**
     * 默认延迟3秒
     */
    private static final long DELAY_MS = 1000L * 3;

    /**
     * 订单id
     */
    private final String orderId;

    /**
     * 消息创建的时间戳
     */
    private final long createTime;

    /**
     * 过期时间
     */
    private final long expire;

    public MyDelayMessage(String orderId){
        this.orderId = orderId;
        this.createTime = System.currentTimeMillis();
        this.expire = this.createTime + DELAY_MS;//计算出过期时长

    }

    public MyDelayMessage(String orderId,long expireSec){
        this.orderId = orderId;
        this.createTime = System.currentTimeMillis();
        this.expire = this.createTime + expireSec * 1000L;//计算出过期时长
    }

    /**
     * 返回延迟时长
     */
    @Override
    public long getDelay(TimeUnit unit) {
        //根据当前时间计算
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

}

2. 定义一个操作订单过期的队列

/**
 * 订单延迟对象。用于处理订单过期
 * Created by wingChan on 2020/2/15.
 */
public class MyDelayQueue {
    private static DelayQueue<MyDelayMessage> queue = new DelayQueue<>();

    private MyDelayQueue(){}

    private static class SingletonHolder{

        private  static MyDelayQueue singleton = new MyDelayQueue();
    }
    //单例队列
    public static MyDelayQueue getQueue(){
        return SingletonHolder.singleton;
    }


    public  Boolean  produce(MyDelayMessage message){
        return queue.add(message);
    }

    /**
     * 延迟消费队列,取不到的话会阻塞一直到队列有消息再被唤醒。之后再取消息
     */
    public  MyDelayMessage consume() throws InterruptedException {
        return queue.take();
    }



}

3. 定义一个后台任务,不停从队列中取消息进行消费


/**
 * 处理过期订单后台任务
 * Created by wingChan on 2020/2/15.
 */
@Component
public class OrderCancelTask implements ApplicationRunner {

    /**
     * 在容器启动完成的时候,会执行一个线程。从MyDelayQueue中取消息
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("========开启处理过期订单线程=========");
        new Thread(()->{
            while (true){
                MyDelayMessage msg = null;
                try {
                    msg = MyDelayQueue.getQueue().consume();
                    if(msg != null){
                        //订单过期的业务逻辑。。。。
                        System.out.println("有订单 " + msg.getOrderId() + " 过期");

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

4. 下面给出一个下订单的实例接口。主要是在创建订单后,把当前订单放入延迟队列中。

@Controller
@RequestMapping("order")
public class OrderController extends BaseController {

    /**
     * 下订单
     */
    @PostMapping()
    @ResponseBody
    public WSResponseVO doOrder(){
        //调用生成订单逻辑,并返回订单id
        String orderId = UUID.randomUUID().toString();
        //此处设置过期时间,10s
        MyDelayQueue.getQueue().produce(new MyDelayMessage(orderId,10));
        return operateSuccess();
    }

}

本文到此结束

发布了53 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40085888/article/details/104332068
今日推荐