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();
}
}
本文到此结束