使用DelayQueue模拟订单自动取消功能
背景:
今天要做一个作业:模拟一个使用DelayQueue的场景。于是我就做一个模拟订单自动取消的功能吧。需求以及实现思路如下:
需求:
1. 生成6个订单,从1号到6号订单,它们的创建时间依次递增3秒。
2. 规定如果一个订单在3秒内状态还是“CREATED”状态,那么就改成“CANCELED”状态。
实现思路:
1. 定义订单类,其实现Delayed接口,为啥要实现这个接口呢?是因为存放到DelayQueue里的元素都要实现Delayed接口,用于判断是否到了超时时间。
2. 在main方法里启动两个线程,第一个线程用来往DelayQueue里添加6个订单,这6个订单的创建时间依次递增3秒。另一个线程是循环从DelayQueue里面获取超时的订单,改成取消状态,然后打印出日志出来。超时的条件是该订单的超时时间字段大于或等于当前系统时间。
代码实现:
先定义一个订单类,实现了Delayed接口:
package top.usjava.learn.springbootlearn.vo;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 订单类,用于存放订单头信息
*
* @author Owen
* @date 2018/9/3 14:10
*/
public class Order implements Delayed {
String orderNo;
String receiveName;
int cost;
String status;
Date createTime;
Date cancelTime;
public Order(String orderNo, String receiveName, int cost, String status, Date createTime, Date cancelTime) {
this.orderNo = orderNo;
this.receiveName = receiveName;
this.cost = cost;
this.status = status;
this.createTime = createTime;
this.cancelTime = cancelTime;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getReceiveName() {
return receiveName;
}
public void setReceiveName(String receiveName) {
this.receiveName = receiveName;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getCancelTime() {
return cancelTime;
}
public void setCancelTime(Date cancelTime) {
this.cancelTime = cancelTime;
}
@Override
public long getDelay(TimeUnit unit) {
//下面用到unit.convert()方法,其实在这个小场景不需要用到,只是学习一下如何使用罢了
long l = unit.convert(cancelTime.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return l;
}
@Override
public int compareTo(Delayed o) {
//这里根据取消时间来比较,如果取消时间小的,就会优先被队列提取出来
return this.getCancelTime().compareTo(((Order) o).getCancelTime());
}
}
测试类,先往DelayQueue插入6个订单,然后等待这些订单超时时间到了后,就取出来改状态:
package top.usjava.learn.springbootlearn.testmain;
import top.usjava.learn.springbootlearn.vo.Order;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.DelayQueue;
/**
* 第一周作业:模拟一个使用DelayQueue的场景
* 这里模拟的是订单下达之后,如果一直都还没支付,也就是停留在创建状态的话,就将其改成取消状态。
*
* @author Owen
* @date 2018/8/27 21:20
*/
public class TestDelayQueue {
//是否开启自动取消功能
int isStarted = 1;
//延迟队列,用来存放订单对象
DelayQueue<Order> queue = new DelayQueue();
public static void main(String[] args) {
TestDelayQueue testDelayQueue = new TestDelayQueue();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//新建一个线程,用来模拟定时取消订单job
Thread t1 = new Thread(() -> {
System.out.println("开启自动取消订单job,当前时间:"+ LocalDateTime.now().format(formatter));
while (testDelayQueue.isStarted == 1) {
try {
Order order = testDelayQueue.queue.take();
order.setStatus("CANCELED");
System.out.println("订单:" + order.getOrderNo() + "付款超时,自动取消,当前时间:"+ LocalDateTime.now().format(formatter));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
//新建一个线程,模拟提交订单
Thread t2 = new Thread(() -> {
//定义最早的订单的创建时间
long beginTime = System.currentTimeMillis();
//下面模拟6个订单,每个订单的创建时间依次延后3秒
testDelayQueue.queue.add(new Order("SO001", "A", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
beginTime += 3000L;
testDelayQueue.queue.add(new Order("SO002", "B", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
beginTime += 3000L;
testDelayQueue.queue.add(new Order("SO003", "C", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
beginTime += 3000L;
testDelayQueue.queue.add(new Order("SO004", "D", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
beginTime += 3000L;
testDelayQueue.queue.add(new Order("SO005", "E", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
beginTime += 3000L;
testDelayQueue.queue.add(new Order("SO006", "F", 100, "CREATED", new Date(beginTime), new Date(beginTime + 3000)));
});
t2.start();
}
}
运行结果:
上面的测试类的两个线程几乎是同一时间运行的,因此第一个订单可以认为是2018-09-04 14:19:57这个时间点创建的。然后从打印结果来看,基本上每个订单一旦到了超时时间,就被队列提出出来,进行取消操作了。这样,就完成了这个小小的模拟自动取消订单需求。
题外话:TimeUnit的使用
直接看代码和效果就知道怎么用了:
例子1:
public static void main(String[] args) {
TimeUnit timeUnit = TimeUnit.MINUTES;
long convert = timeUnit.convert(61, TimeUnit.SECONDS);
System.out.println(convert);
}
运行结果:
例子2:
public static void main(String[] args) {
TimeUnit timeUnit = TimeUnit.MINUTES;
long convert = timeUnit.convert(59, TimeUnit.SECONDS);
System.out.println(convert);
}
运行结果:
由此看出,上面例子就是把第一个参数看做是秒数,然后转成相应的分数返回,满60秒就返回1分钟,不满就返回0分钟。其他时间单位转换是同样的用法和效果,例如3601秒就是满1小时,3501秒就是0小时。