DelayQueue 延迟队列使用
一、DelayQueue是什么
DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
二、DelayQueue能做什么
1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。
2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。
3.关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
4.缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
5.任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求等。
三、实例展示
定义元素类,作为队列的元素
DelayQueue只能添加(offer/put/add)实现了Delayed接口的对象,意思是说我们不能想往DelayQueue里添加什么就添加什么,不能添加int、也不能添加String进去,必须添加我们自己的实现了Delayed接口的类的对象,来代码:
/**
* compareTo 方法必须提供与 getDelay 方法一致的排序
*/
class MyDelayedTask implements Delayed{
private String name ;
private long start = System.currentTimeMillis();
private long time ;
public MyDelayedTask(String name,long time) {
this.name = name;
this.time = time;
}
/**
* 需要实现的接口,获得延迟时间 用过期时间-当前时间
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((start+time) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
/**
* 用于延迟队列内部比较排序 当前时间的延迟时间 - 比较对象的延迟时间
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
MyDelayedTask o1 = (MyDelayedTask) o;
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "MyDelayedTask{" +
"name='" + name + '\'' +
", time=" + time +
'}';
}
}
其中,compareTo 方法 getDelay 方法 就是Delayed接口的方法,我们必须实现,而且按照JAVASE文档,compareTo 方法必须提供与 getDelay 方法一致的排序,也就是说compareTo方法里可以按照getDelay方法的返回值大小排序,即在compareTo方法里比较getDelay方法返回值大小
写main方法测试
定义一个DelayQueue,添加几个元素,while循环获取元素
private static DelayQueue delayQueue = new DelayQueue();
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
delayQueue.offer(new MyDelayedTask("task1",10000));
delayQueue.offer(new MyDelayedTask("task2",3900));
delayQueue.offer(new MyDelayedTask("task3",1900));
delayQueue.offer(new MyDelayedTask("task4",5900));
delayQueue.offer(new MyDelayedTask("task5",6900));
delayQueue.offer(new MyDelayedTask("task6",7900));
delayQueue.offer(new MyDelayedTask("task7",4900));
}
}).start();
while (true) {
Delayed take = delayQueue.take();
System.out.println(take);
}
}
执行结果
DelayQueue属于排序队列,它的特殊之处在于队列的元素必须实现Delayed接口,该接口需要实现compareTo和getDelay方法。
static class Task implements Delayed{
@Override
//比较延时,队列里元素的排序依据
public int compareTo(Delayed o) {
return 0;
}
@Override
//获取剩余时间
public long getDelay(TimeUnit unit) {
return 0;
}
}
元素进入队列后,先进行排序,然后,只有getDelay也就是剩余时间为0的时候,该元素才有资格被消费者从队列中取出来,所以构造函数一般都有一个时间传入。
另一个例子:
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 定时创建
*/
@Slf4j
@Component
public class TestJob implements Runnable {
@Resource
private OrderService orderService;
private static final DelayQueue<DelayElement> DELAY_QUEUE = new DelayQueue<>();
/**
* 定时扫描
*/
@XxlJob("testJobHandler")
public void autoGenerateDoJob() {
log.info("开始执行扫描");
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 获取需要添加延时处理的数据
List<Order> orderList = orderService.getOrderList();
for (Order one : orderList) {
// 获取执行时间
LocalDateTime time = LocalDateTime.parse(one.getNextTime(), format);
// 获取延迟时间
Duration duration = Duration.between(LocalDateTime.now(), time);
DELAY_QUEUE.put(new PlanJob.DelayElement(duration.toMillis(), order));
// 更新下次执行时间
String nextTime = "下次执行时间";
Order update = new Order();
update.setId(order.getId());
update.setNextTime(nextTime);
orderService.updateById(update);
}
log.info("执行扫描结束...");
}
@PostConstruct
public void init() {
new Thread(this).start();
}
@Override
public void run() {
synchronized (this) {
while (true) {
// 执行延迟任务
try {
// 获取延时数据,执行操作
DelayElement take = DELAY_QUEUE.take();
Order order = take.getOrder();
// todo 执行操作
} catch (Exception e) {
log.info("任务失败", e.getMessage(), e);
}
}
}
}
@Data
static class DelayElement implements Delayed {
long delayTime = System.currentTimeMillis();
private Order order;
public DelayElement(long delayTime, Order order) {
this.delayTime = this.delayTime + delayTime;
this.order = order;
}
@Override
public long getDelay(@NotNull TimeUnit unit) {
return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(@NotNull Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
}
}