DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
加入其中的元素必需实现Delayed接口。当生产者线程调用put之类的方法加入元素时,会触发Delayed接口中的compareTo方法进行排序,也就是说队列中元素的顺序是按到期时间排序的,而非它们进入队列的顺序。排在队列头部的元素是最早到期的,越往后到期时间赿晚。
直接上代码:
首先定义业务类:
class Order implements Delayed {
private long time;
String name;
public Order(String name, long time) {
this.name = name;
// 如果需要修改时间格式 此处对定义的时间自行约定转化即可 此处定义秒
this.time = TimeUnit.SECONDS.toMillis(time) + System.currentTimeMillis();
}
@Override
public long getDelay(TimeUnit unit) {
long diffTime = time - System.currentTimeMillis();
return diffTime;
}
@Override
public int compareTo(Delayed o) {
Order item = (Order) o;
long diff = this.time - item.time;
return diff >= 0 ? 1 : -1;
}
public String getName() {
return name;
}
}
定义一个Order类,关键字段时间,name可以在实际业务中定义为你需要适用的数据实体,OBJ也可。实现Delayed,getDelay用于校验当前时间是否超过设定时间,compareTo会对超过时间的元素进行排序,他的排序方式需要定义为getDelay一致。
接下来我们上测试代码:
public static void main(String[] args) throws InterruptedException {
//实际业务中 对于延迟任务来说,一把会放在线程池中自行处理 因此提前设置
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 此处定义秒
Order order1 = new Order("order1", 1);
Order order2 = new Order("order2", 5);
Order order3 = new Order("order3", 3);
DelayQueue<Order> queue = new DelayQueue<>();
queue.put(order1);
queue.put(order2);
queue.put(order3);
System.out.println("开始时间 当前时间:" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
Callable<Integer> task;
task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
for (; ; ) {
if (CollectionUtils.isEmpty(queue)) {
break;
}
Order take = queue.take();
if (take == null) {
break;
}
System.out.println(take.getName()+" 当前时间:" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
return 1;
}
};
List<Callable<Integer>> tasks = new ArrayList<>();
tasks.add(task);
//提交任务 并关闭线程
executorService.invokeAll(tasks);
executorService.shutdown();
}
执行结果:
开始时间 当前时间:2021-09-07T11:18:58.568
order1 当前时间:2021-09-07T11:18:59.486
order3 当前时间:2021-09-07T11:19:01.486
order2 当前时间:2021-09-07T11:19:03.486
如果需要改为毫秒级别计算的话,构造器中不要转化即可。
但是如果是单体项目,建议将数据持久化,并且开线程处理,因为该队列使用内存存储,如果一旦出现异常,比如宕机,name存储的重要数据将会小时,实际业务中,会使用到MQ或者Redis进行处理,用于避免重要数据的丢失。
个人见解,不喜勿喷!