业务网站建设,安徽网站开发与维护专业,58同城百姓网,什么是网站域名文章目录 需求创建订单类创建延时队列优缺点 Reference JDK
DelayQueue是一个无阻塞队列#xff0c;底层是
PriorityQueue 需求
经典的订单超时取消
创建订单类
放入DelayQueue的对象需要实现Delayed接口
public interface Delayed extends ComparableDelayed {… 文章目录 需求创建订单类创建延时队列优缺点 Reference JDK
DelayQueue是一个无阻塞队列底层是
PriorityQueue 需求
经典的订单超时取消
创建订单类
放入DelayQueue的对象需要实现Delayed接口
public interface Delayed extends ComparableDelayed {long getDelay(TimeUnit unit);
}可以看到Delayed包含一个getDelay抽象方法同时继承了ComparableDelayed接口因此要实现Delayed接口需要实现getDelay和ComparableDelayed两个抽象方法最后完成订单类CancelOrder实现Delayed接口
public class CancelOrder implements Delayed {// 订单号private String orderNo;// 过期时间 nano secondsprivate long timeout;public CancelOrder(String orderNo, long timeout) {this.orderNo orderNo;this.timeout timeout System.nanoTime();}Overridepublic String toString() {return CancelOrder{ orderNo orderNo \ , timeout timeout };}Overridepublic long getDelay(TimeUnit unit) {// 以JVM高分辨率时间源的值为参考获取过期时刻return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS);}Overridepublic int compareTo(Delayed o) {if (o this){return 0;}CancelOrder t (CancelOrder) o;long d (getDelay(TimeUnit.NANOSECONDS) - t.getDelay(TimeUnit.NANOSECONDS));return (d 0) ? 0 : ((d 0)? -1 : 1);}
}这里有几个地方需要啰嗦下
订单类CancelOrder包含两个成员属性 orderNo订单编号timeout过期时间单位为纳秒(1ns 10^-9s)所以要用long getDelay()方法用于获取订单过期的时刻订单过期时刻是以JVM的时间作为起点计算的 System.nanoTime(): 返回正在运行的Java虚拟机的高分辨率时间源的当前值以纳秒计订单过期的时刻 JVM时间源的当前值过期时间timeout compareTo方法就是实现了优先队列的比较方法根据各个订单的过期时刻排序这里其实就是一个小顶堆队头为过期时刻最小的订单。
创建延时队列
创建一个DelayQueue其实就跟创建PriorityQueue差不多只不过这里不需要重写个Comparator因为订单对象已经重写了CompareTo了是一个队头为最早过期过期时刻最小的元素的小顶堆。
下面主要用到DelayQueue的两个方法分别用于加入/取出订单
put(): 非常亲切的入队方法跟普通队列一样将对象加入队尾take(): 取出队头【过期】对象不是跟poll()一样直接取出了
弄个测试类测试一下
public class DelayQueueTest {public static void main(String[] args) throws InterruptedException {DelayQueueCancelOrder queue new DelayQueue();// 每秒生成1个订单共生成5个订单for (int i 0; i 5; i){// 10s过期CancelOrder cancelOrder new CancelOrder(orderNoi, TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS));// 获取当前时间String time LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss));System.out.println(time : 生成订单, 10s有效order: cancelOrder);// 将订单放入延时队列queue.put(cancelOrder);// 控制每秒生成一个订单Thread.sleep(1000);}// 延时队列取出超时订单try {while (!queue.isEmpty()){// 轮询获取队头过期元素CancelOrder order queue.take();// 获取当前时间String timeout LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss));System.out.println(timeout : 订单超时orderorder);}} catch (InterruptedException e){throw new RuntimeException(e);}}
}输出
2024-03-30 18:54:37: 生成订单, 10s有效order: CancelOrder{orderNoorderNo0, timeout636498762218800}
2024-03-30 18:54:38: 生成订单, 10s有效order: CancelOrder{orderNoorderNo1, timeout636499784320900}
2024-03-30 18:54:39: 生成订单, 10s有效order: CancelOrder{orderNoorderNo2, timeout636500788490700}
2024-03-30 18:54:40: 生成订单, 10s有效order: CancelOrder{orderNoorderNo3, timeout636501792751100}
2024-03-30 18:54:41: 生成订单, 10s有效order: CancelOrder{orderNoorderNo4, timeout636502796614500}
2024-03-30 18:54:47: 订单超时orderCancelOrder{orderNoorderNo0, timeout636498762218800}
2024-03-30 18:54:48: 订单超时orderCancelOrder{orderNoorderNo1, timeout636499784320900}
2024-03-30 18:54:49: 订单超时orderCancelOrder{orderNoorderNo2, timeout636500788490700}
2024-03-30 18:54:50: 订单超时orderCancelOrder{orderNoorderNo3, timeout636501792751100}
2024-03-30 18:54:51: 订单超时orderCancelOrder{orderNoorderNo4, timeout636502796614500}优缺点
优点
简单不需要借助其他第三方组件成本低适合单体应用
缺点
不适合数据量较大的场景所有可能超时的数据都要进入DelayQueue中全部保存在JVM内存中内存开销大可能引发内存溢出无法持久化因为存在JVM内存中不像Redis可以通过AOF或者RDB宕机数据就丢失了无法较好地适配分布式集群真要分布式就只能在集群中选一台leader专门处理效率低
Reference
订单超时自动取消的技术方案解析及代码实现