持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
Java 单机延迟任务我们可以通过, Delayed + DelayQueue 实现
实现 Delayed 接口
实现 Delayed 接口 ,自定义消息体/延迟任务对象
- activeTime 是延迟任务到期时间,单位 ms
- data 是一个业务实体,泛型结构,我们可以自定义结构体,或者类型。
- compareTo() 方法是按照执行时间排序
- getDelay() 获取到期时间
public class DelayMessage<T> implements Delayed {
/**
* 到期时间 单位:ms
*/
private long activeTime;
/**
* 业务实体
*/
private T data;
public DelayMessage(long activeTime, T data) {
// 将传入的时间转换为超时的时刻
this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime, TimeUnit.MILLISECONDS)
+ System.nanoTime();
this.data = data;
}
public long getActiveTime() {
return activeTime;
}
public T getData() {
return data;
}
/**
* 按照剩余时间进行排序
*
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
// 订单剩余时间-当前传入的时间= 实际剩余时间(单位纳秒)
long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
// 根据剩余时间判断等于0 返回1 不等于0
// 有可能大于0 有可能小于0 大于0返回1 小于返回-1
return (d == 0) ? 0 : ((d > 0) ? 1 : -1);
}
/**
* 获取剩余时间
*
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
// 剩余时间= 到期时间-当前系统时间,系统一般是纳秒级的,所以这里做一次转换
return unit.convert(activeTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
}
创建任务任务执行方法
创建任务执行线程、方法,去调用 delayQueue
获取延迟任务对象。 通常情况下我们可以定义一个独立的线程去处理这些任务。我们可以使用 Jdk8 的拉姆达表达式来实现线程的包装。
/**
* 延时任务执行线程
*/
private void executeThread() {
while (true) {
DelayMessage<Object> task = null;
try {
task = delayQueue.take();
processTask(task);
} catch (Exception e) {
System.err.println("延时任务执行失败 task:" + task + " err:" + e);
}
}
}
/**
* 内部执行延时任务
*
* @param task
*/
private void processTask(DelayMessage<Object> task) {
if (task == null) {
return;
}
Object data = task.getData();
System.out.println("out:" + data + " run time:" + System.currentTimeMillis());
}
管理 DelayQueue 任务
DelayQueueManager 类主要是用来管理 DelayQueue
到期任务的执行,我们在构造方法中创建了一个子线程对 delayQueue.take();
进行轮询获取到期任务,如果存在到期任务就进行执行:
PS:当前实验代码只适用于小任务量的场景,如果我们的任务比较多的话,我们可以把逻辑执行的方法换成一个线程提交逻辑,进行异步处理,防止 take 线程阻塞。
public class DelayQueueManager {
private DelayQueue<DelayMessage<Object>> delayQueue = new DelayQueue<>();
private static DelayQueueManager instance = new DelayQueueManager();
public static DelayQueueManager getInstance() {
return instance;
}
private DelayQueueManager() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Thread(this::executeThread));
}
/**
* 加入到延时队列中
*
* @param task
*/
public void put(DelayMessage<Object> task) {
System.out.println("加入延时任务 delay= " + task.getDelay(TimeUnit.MILLISECONDS) + "ms");
delayQueue.put(task);
}
/**
* 延时任务执行线程
*/
private void executeThread() {
while (true) {
DelayMessage<Object> task = null;
try {
task = delayQueue.take();
processTask(task);
} catch (Exception e) {
System.err.println("延时任务执行失败 task:" + task + " err:" + e);
}
}
}
/**
* 内部执行延时任务
*
* @param task
*/
private void processTask(DelayMessage<Object> task) {
if (task == null) {
return;
}
Object data = task.getData();
System.out.println("out:" + data + " run time:" + System.currentTimeMillis());
}
}
测试程序
下面是一个简单的测试代码,我们提交三个延迟消息。然后在观察我们的打印信息.
测试代码如下:
public static void main(String[] args) {
DelayQueueManager delayQueueManager = new DelayQueueManager();
System.out.println("start " + System.currentTimeMillis());
delayQueueManager.put(new DelayMessage<>(5000L, "Hello Tom"));
delayQueueManager.put(new DelayMessage<>(8000L, "Hello Ktm"));
delayQueueManager.put(new DelayMessage<>(2000L, "Hello Star"));
}
输出结果如下: