免费毕业设计的网站建设招标网站的服务费怎么做分录
文章目录
- 需求
 - 创建订单类
 - 创建延时队列
 - 优缺点
 
- Reference
 
JDK
DelayQueue是一个无阻塞队列,底层是 
PriorityQueue 
 
需求
经典的订单超时取消
创建订单类
放入DelayQueue的对象需要实现Delayed接口
public interface Delayed extends Comparable<Delayed> {long getDelay(TimeUnit unit);
}
 
可以看到,Delayed包含一个getDelay抽象方法,同时继承了Comparable<Delayed>接口,因此要实现Delayed接口需要实现getDelay和Comparable<Delayed>两个抽象方法,最后完成订单类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 {DelayQueue<CancelOrder> queue = new DelayQueue<>();// 每秒生成1个订单,共生成5个订单for (int i = 0; i < 5; i++){// 10s过期CancelOrder cancelOrder = new CancelOrder("orderNo"+i, 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 + ": 订单超时,order:"+order);}} catch (InterruptedException e){throw  new RuntimeException(e);}}
}
 
输出:
2024-03-30 18:54:37: 生成订单, 10s有效,order: CancelOrder{orderNo='orderNo0', timeout=636498762218800}
2024-03-30 18:54:38: 生成订单, 10s有效,order: CancelOrder{orderNo='orderNo1', timeout=636499784320900}
2024-03-30 18:54:39: 生成订单, 10s有效,order: CancelOrder{orderNo='orderNo2', timeout=636500788490700}
2024-03-30 18:54:40: 生成订单, 10s有效,order: CancelOrder{orderNo='orderNo3', timeout=636501792751100}
2024-03-30 18:54:41: 生成订单, 10s有效,order: CancelOrder{orderNo='orderNo4', timeout=636502796614500}
2024-03-30 18:54:47: 订单超时,order:CancelOrder{orderNo='orderNo0', timeout=636498762218800}
2024-03-30 18:54:48: 订单超时,order:CancelOrder{orderNo='orderNo1', timeout=636499784320900}
2024-03-30 18:54:49: 订单超时,order:CancelOrder{orderNo='orderNo2', timeout=636500788490700}
2024-03-30 18:54:50: 订单超时,order:CancelOrder{orderNo='orderNo3', timeout=636501792751100}
2024-03-30 18:54:51: 订单超时,order:CancelOrder{orderNo='orderNo4', timeout=636502796614500}
 
优缺点
优点:
- 简单,不需要借助其他第三方组件,成本低,适合单体应用
 
缺点:
- 不适合数据量较大的场景:所有可能超时的数据都要进入
DelayQueue中,全部保存在JVM内存中,内存开销大,可能引发内存溢出 - 无法持久化:因为存在JVM内存中,不像Redis可以通过AOF或者RDB,宕机数据就丢失了
 - 无法较好地适配分布式集群:真要分布式就只能在集群中选一台leader专门处理,效率低
 
Reference
订单超时自动取消的技术方案解析及代码实现
