一,延迟任务应用场景?
一般用于处理订单,将redis中的数据延迟存入数据库,实现异步存储减少DB的压力
DelayQueue是基于内存的延迟队列
二, 延迟任务的实现方案有很多
DelayQueue
Redisson
MQ
时间轮
原理
JDK自带延迟队列,基于阻塞队列实现。
基于Redis数据结构模拟JDK的DelayQueue实现
利用MQ的特性。例如RabbitMQ的死信队列
时间轮算法
优点
不依赖第三方服务
分布式系统下可用
不占用JVM内存
分布式系统下可以
不占用JVM内存
不依赖第三方服务
性能优异
缺点
占用JVM内存
只能单机使用
依赖第三方服务
依赖第三方服务
只能单机使用
三,延迟任务的原理
1,DelayQueue的源码
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
// ... 略
}
- DelayQueue实现了BlockingQueue接口,是一个阻塞队列。队列就是容器,用来存储东西的.
DelayQueue的泛型定义
<E extends Delayed>
这说明存入
DelayQueue
内部的元素必须是Delayed
类型
进一步查看Delay接口
public interface Delayed extends Comparable<Delayed> {
/**
* Returns the remaining delay associated with this object, in the
* given time unit.
*
* @param unit the time unit
* @return the remaining delay; zero or negative values indicate
* that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
}
看完之后我们先不管 ,继续看他的父类Comparable<Delayed>
里面有一个方法,用于比较大小
2,从源码中可以看出,Delayed类型必须具备两个方法:
getDelay()
:获取延迟任务的剩余延迟时间
compareTo(T t)
:比较两个延迟任务的延迟时间,判断执行顺序
根据源码中的注解可以看出
zero or negative values indicate * that the delay has already elapsed
零或者负值表示延迟已经过去
四,DelayQueue的用法
1,定义一个延迟任务的工具类
package com.tianji.learning.utils;
import lombok.Data;
import java.time.Duration;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@Data
public class DelayTask<D> implements Delayed {
private D data; //用于存放延迟任务的数据
private long deadlineNanos; //延迟任务延迟到什么时候
/**
* delayTime 延迟多长时间
*/
public DelayTask(D 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 l = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
if(l > 0){
return 1;
}else if(l < 0){
return -1;
}else {
return 0;
}
}
}
2,使用延迟队列
// 1.初始化延迟队列
DelayQueue<DelayTask<String>> queue = new DelayQueue<>();
// 2.向队列中添加延迟执行的任务 //当前延迟时间是Second
queue.add(new DelayTask<>("延迟任务数据,可以是方法", Duration.ofSeconds(延迟时间)));
// 3.尝试执行任务
DelayTask<String> task = queue.take();
//执行延迟队列中的任务
task.getData()