Java延迟队列
📌 1. 场景背景
最近做项目,使用到了延迟队列。场景是这样的:在在线视频学习中,学生每隔几秒上报当前学习进度,为避免频繁写数据库、提升性能,采用以下方案:
先写入 Redis,再延迟一定时间(如20秒)后持久化到数据库。
⚙️ 2. 技术方案概览
使用 Java 提供的 DelayQueue
处理延迟任务。
关键组成:
组件 | 说明 |
---|---|
DelayTask<T> | 封装延迟任务,定义触发时间 |
DelayQueue | 用于存放所有的延迟任务,按时间顺序触发 |
LearningRecordsDelayTaskHandler | 延迟任务的核心调度类 |
🧩 3. DelayTask<T>
延迟任务封装类
public class DelayTask<T> implements Delayed {
private T data;
private long deadlineNanos;
public DelayTask(T data, Duration delayTime) {
this.data = data;
this.deadlineNanos = System.nanoTime() + delayTime.toNanos();
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(Math.max(0, deadlineNanos - System.nanoTime()), TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
long diff = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
}
}
✅ 说明:
deadlineNanos
:任务到期时间(单位:纳秒)- 实现
Delayed
接口,用于DelayQueue
排序与调度
🧠 4. LearningRecordsDelayTaskHandler
任务处理类
主要职责:
- 管理延迟任务队列
- 处理过期任务
- 管理 Redis 缓存的读写
队列定义:
private final DelayQueue<DelayTask<RecordTaskData>> queue = new DelayQueue<>();
🧵 5. 初始化与销毁
@PostConstruct
public void init() {
CompletableFuture.runAsync(this::handleDelayTask);
}
@PreDestroy
public void destroy() {
begin = false;
}
- 使用异步线程后台监听队列
- 应用关闭时中止处理线程
🔄 6. 延迟任务处理流程
while (begin) {
DelayTask<RecordTaskData> task = queue.take();
...
}
流程图:
前端上报进度
↓
写入 Redis 缓存
↓
提交延迟任务(20秒后)
↓
20秒后从 DelayQueue 取出任务
↓
比较任务数据与当前 Redis 数据
→ 相同:说明用户已停留 → 写库
→ 不同:用户仍在更新 → 放弃
💾 7. Redis 缓存结构与读写
- 键:
learning:record:{lessonId}
- 字段:
{sectionId}
- 值:
RecordCacheData
的 JSON 字符串
写入缓存:
String key = "learning:record:" + lessonId;
redisTemplate.opsForHash().put(key, sectionId, json);
redisTemplate.expire(key, Duration.ofMinutes(1));
读取缓存:
Object cacheData = redisTemplate.opsForHash().get(key, sectionId.toString());
📤 8. 添加任务到队列
queue.add(new DelayTask<>(new RecordTaskData(record), Duration.ofSeconds(20)));
- 提交一个包含用户观看记录的延迟任务,20秒后触发处理
✅ 9. 优点总结
优点 | 描述 |
---|---|
⏳ 延迟写库 | 降低数据库压力,避免频繁更新 |
🔁 防抖处理 | 用户频繁上报只处理最后一次 |
🧠 最终一致性 | 延迟处理保证数据最终落库 |
💡 简单高效 | 基于 JDK 自带 DelayQueue,无需额外中间件 |