Java延时队列

在业务开发中常常存在超时处理机制,比如订单多久没支付自动取消,命令执行太久自动取消执行之类。可以使用redis的key过期订阅机制,也可以使用rabbitmq的死信队列,亦或是循环轮训数据库等等来实现。在业务量小,无中间件的情况下,就可以考虑使用java原生的延时队列来实现,如下:

package com.example.quene;

import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class DelayQuene {

    public static final DelayQueue<DelayedOrder> DELAY_QUEUE = new DelayQueue<>();

    @Data
    public static class DelayedOrder implements Delayed {

        private int index;

        private long timeout;

        public DelayedOrder(long timeout) {
            this.timeout = timeout + System.nanoTime();
        }

        @Override
        public int compareTo(Delayed other) {
            if (other == this) {
                return 0;
            }
            long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));
            return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
        }

        // 返回距离你自定义的超时时间还有多少
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS);
        }

        public void excute() {
            log.info("第{}个订单已经到期未支付,自动取消", index);
        }

    }

    public static void main(String[] args) {

        for (int i = 1, len = 10; i <= len; i++) {
            //延时队列放入10个订单,订单的过期时间分别为 1秒,2秒,3秒,4秒.......
            DelayedOrder order = new DelayedOrder(TimeUnit.SECONDS.toNanos(i));
            order.setIndex(i);
            DELAY_QUEUE.put(order);
        }

        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                while (true) {
                    //阻塞方法,无订单过期时阻塞等待
                    DelayedOrder take = DELAY_QUEUE.take();
                    take.excute();
                }
            }
        }).start();

    }

}