详解Java线程池队列中的延迟队列DelayQueue
作者:刨红薯的小羊竿尔 时间:2023-08-30 01:22:04
在阻塞队里中,除了对元素进行增加和删除外,我们可以把元素的删除做一个延迟的处理,即使用DelayQueue的方法。本文就来和大家聊聊Java线程池队列中的DelayQueue—延迟队列
public enum QueueTypeEnum {
ARRAY_BLOCKING_QUEUE(1, "ArrayBlockingQueue"),
LINKED_BLOCKING_QUEUE(2, "LinkedBlockingQueue"),
DELAY_QUEUE(3, "DelayQueue"),
PRIORITY_BLOCKING_QUEUE(4, "PriorityBlockingQueue"),
SYNCHRONOUS_QUEUE(5, "SynchronousQueue"),
LINKED_TRANSFER_QUEUE(6, "LinkedTransferQueue"),
LINKED_BLOCKING_DEQUE(7, "LinkedBlockingDeque"),
VARIABLE_LINKED_BLOCKING_QUEUE(8, "VariableLinkedBlockingQueue"),
MEMORY_SAFE_LINKED_BLOCKING_QUEUE(9, "MemorySafeLinkedBlockingQueue");
}
DelayQueue延迟队列
类似于PriorityBlockingQueue,是二叉堆实现的 * 优先级阻塞队列。要求元素都实现Delayed 接口,通过执行时延从队列中提取任务,只有在延迟期满后才能从中提取元素。DelayQueue的泛型参数需要实现Delayed接口,Delayed接口继承了Comparable接口,DelayQueue内部使用非线程安全的优先队列(PriorityQueue),并使用Leader/Followers模式,最小化不必要的等待时间。DelayQueue不允许包含null元素。
public interface Delayed extends Comparable<Delayed> {
/**
* 返回与此对象关联的剩余延迟(给定的时间单位)。
* @param unit 时间单位
* @返回剩余延迟;零值或负值表示 延迟已过期
*/
long getDelay(TimeUnit unit);
}
DelayQueue使用场景
缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
DelayQueue属性
//可重入同步锁
private final transient ReentrantLock lock = new ReentrantLock();
//DelayQueue的实现依赖于PriorityQueue(优先队列)
private final PriorityQueue<E> q = new PriorityQueue<E>();
//第一个等待某个延时对象的线程,在延时对象还没有到期时其他线程看到这个leader不为null,那么就直接wait
//主要是为了避免大量线程在同一时间点唤醒,导致大量的竞争,反而影响性能
private Thread leader = null;
//条件队列,用于wait线程
private final Condition available = lock.newCondition();
DelayQueue构造方法
//从上面属性就可以看出,DelayQueue采用了饿汉模式,调用构造方法即创建了队列实例
public DelayQueue() {}
/**
* 创建一个DelayQueue,最初包含给定的Collection实例集合。
* @param c 最初包含的元素集合
*/
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
实现Delayed接口使用示例
class MyDelay<T> implements Delayed {
long delayTime; // 延迟时间
long expire; // 过期时间
T data;
public MyDelay(long delayTime, T t) {
this.delayTime = delayTime;
// 过期时间 = 当前时间 + 延迟时间
this.expire = System.currentTimeMillis() + delayTime;
data = t;
}
/**
* 剩余时间 = 到期时间 - 当前时间
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
/**
* 优先级规则:两个任务比较,时间短的优先执行
*/
@Override
public int compareTo(Delayed o) {
long f = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
return (int) f;
}
@Override
public String toString() {
return "delayTime=" + delayTime +
", expire=" + expire +
", data=" + data;
}
}
public class DelayQueueDemo {
static BlockingQueue<Delayed> queue = new DelayQueue();
public static void main(String[] args) throws InterruptedException {
queue.add(new MyDelay(8, "第一次添加任务"));
queue.add(new MyDelay(3, "第二次添加任务"));
queue.add(new MyDelay(5, "第三次添加任务"));
while (!queue.isEmpty()) {
Delayed delayed = queue.take();
System.out.println(delayed);
}
}
}
DelayQueue总结
DelayQueue其实采用了装饰器模式,在对PriorityQueue进行包装下增加了延时时间获取元素的功能,其主要特点归纳如下:
DelayQueue是一个 * 阻塞队列,队列内部使用PriorityQueue来实现
进入队列的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素,只有在延迟期满时才能从中提取元素
该队列头部是延迟期满后保存时间最长的Delayed元素
如果没有延迟未过期元素,且队列没有头部,并且poll将返回null
当一个元素的getDelay(TimeUnit.NANOSECONDS)方法返回一个小于等于0的值时,表示该元素已过期
无法使用poll或take移除未到期的元素,也不会将这些元素作为正常元素对待;例如:size方法返回到期和未到期元素的计数之和
此队列不允许使用null元素
来源:https://juejin.cn/post/7173147657316073480
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Java简单验证身份证功能示例
![](https://img.aspxhome.com/file/2023/4/57924_0s.png)
详解Java Proxy动态 代理机制
![](https://img.aspxhome.com/file/2023/6/57726_0s.png)
JAVA实现账户取款和存款操作
Java线程状态变换过程代码解析
![](https://img.aspxhome.com/file/2023/1/57951_0s.png)
springboot 使用自定义的aspect的示例代码
![](https://img.aspxhome.com/file/2023/7/57947_0s.png)
Vs2022环境下安装低版本.net framework的实现步骤
![](https://img.aspxhome.com/file/2023/8/78988_0s.png)
Flutter Widgets粘合剂CustomScrollView NestedScrollView滚动控件
![](https://img.aspxhome.com/file/2023/9/120089_0s.webp)
Android RecyclerView基本使用详解
![](https://img.aspxhome.com/file/2023/6/86016_0s.gif)
AsyncTask官方文档教程整理
六款值得推荐的android(安卓)开源框架简介
MyBatis配置的应用与对比jdbc的优势
![](https://img.aspxhome.com/file/2023/1/58281_0s.png)
Spring @Conditional通过条件控制bean注册过程
如何在IDE部署springboot项目(有swagger和无swagger都是一样的)到服务器或者虚拟机上的docker
![](https://img.aspxhome.com/file/2023/5/58185_0s.png)
Android Camera+SurfaceView自动聚焦防止变形拉伸
![](https://img.aspxhome.com/file/2023/6/82396_0s.png)
C#异步编程Task的创建方式
![](https://img.aspxhome.com/file/2023/1/66591_0s.png)
java8到java15的新功能简介
Java 根据网址查询DNS/IP地址的方法
Dubbo实现分布式日志链路追踪
![](https://img.aspxhome.com/file/2023/2/58292_0s.png)
一文搞懂MyBatis多数据源Starter实现
![](https://img.aspxhome.com/file/2023/5/57525_0s.webp)
Android如何让WebView中的HTML5页面实现视频全屏播放
![](https://img.aspxhome.com/file/2023/1/82401_0s.jpg)