DelayQueue
是 Java 中的一个特殊队列,它是一个无界阻塞延时队列,用于存放实现了 Delayed
接口的元素。这些元素只有在其指定的延时时间到期后才能被取出。DelayQueue
的内部是使用优先级堆来实现的,可以确保队列中的元素按照延时时间的顺序排列。
DelayQueue
的主要特点和作用包括:
-
延时元素存储:
DelayQueue
存放的元素必须是实现了Delayed
接口的对象。每个元素都有一个延时时间,只有在延时时间到期后才能被取出。 -
按延时时间排序: 队列中的元素按照延时时间的顺序排列,即延时时间最短的元素位于队列的头部,延时时间最长的元素位于队列的尾部。
-
阻塞操作:
DelayQueue
提供了阻塞式的取元素操作,如果队列为空,调用take()
方法将会阻塞直到有元素可以取出。 -
线程安全:
DelayQueue
是线程安全的,可以在多线程环境下安全地使用。
DelayQueue
的典型应用场景包括:
- 延时任务调度: 可以将需要延时执行的任务封装成实现
Delayed
接口的对象,然后放入DelayQueue
中进行调度和执行。 - 缓存过期清理: 可以将缓存中的对象封装成实现
Delayed
接口的对象,并设置它们的过期时间,然后放入DelayQueue
中。通过定期从队列中取出过期的对象进行清理,可以实现缓存的过期清理功能。
总的来说,DelayQueue
提供了一种方便的机制来实现延时任务调度和处理延时元素,是 Java 多线程编程中的重要组件之一。
1、通用延时任务类
/**
* @author Lucas
* date 2024/4/8 21:13
* description 延时任务
*/
@Data
public class DelayTask<T> implements Delayed {
/**
* 消息内容
*/
private T data;
/**
* 执行时间戳
*/
private long expire;
/**
* 剩余时间 = 执行时间 - 当前时间
* @param unit the time unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(), unit);
}
/**
* 优先级规则:两个任务比较,时间短的优先执行
* @param o the object to be compared.
* @return
*/
@Override
public int compareTo(Delayed o) {
long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
return (int) delta;
}
}
2、消息内容类(根据自身业务场景定义)
/**
* @author Lucas
* date 2024/4/8 21:21
* description 消息内容(根据自身业务场景定义)
*/
@Data
public class TaskBase {
/**
* ID
*/
private Integer id;
/**
* 名称
*/
private String name;
}
3、延迟队列管理器类
/**
* @author Lucas
* date 2024/4/8 21:21
* description 延迟队列管理器
*/
@Slf4j
@Configuration
public class DelayQueueManager<T> implements CommandLineRunner {
private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
/**
* 加入延时队列
* @param message 消息内容
* @param expire 执行时间戳
*/
public void put(T message, long expire) {
log.info("加入DelayQueue延时任务,消息内容:{}", JSONObject.toJSONString(message));
DelayTask delayTask = new DelayTask();
delayTask.setData(message);
delayTask.setExpire(expire);
delayQueue.put(delayTask);
}
/**
* 取消延时任务
* @param message 消息内容
* @param expire 执行时间戳
* @return
*/
public boolean remove(T message, long expire) {
log.info("取消DelayQueue延时任务,消息内容:{}", JSONObject.toJSONString(message));
DelayTask delayTask = new DelayTask();
delayTask.setData(message);
delayTask.setExpire(expire);
return delayQueue.remove(delayTask);
}
/**
* 推送的消息内容可能持久化到数据库,避免系统重启导致消息丢失
* 系统重启在此获取数据重新加载到队列中
* @param args incoming main method arguments
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
log.info("服务重启,初始化延时队列信息");
//单线程的线程池中执行,适用于需要顺序执行任务或者保证线程安全的场景
Executors.newSingleThreadExecutor().execute(new Thread(this::executeThread));
}
/**
* 延时任务执行线程
*/
private void executeThread() {
while (true) {
try {
DelayTask task = delayQueue.take();
log.info("执行延时任务,内容:{}", JSONObject.toJSONString(task));
} catch (InterruptedException e) {
break;
}
}
}
}
4、测试(根据自身业务场景实现)
private final DelayQueueManager<TaskBase> delayQueueManager;
TaskBase taskBase = new TaskBase();
taskBase.setId(123);
taskBase.setName("Test1234567890");
long date = new Date().getTime() + 10000;
//加入延时队列
delayQueueManager.put(taskBase,date);
//移除延时队列
delayQueueManager.remove(taskBase,date);