详解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


猜你喜欢
- 效果图白话分析:多线程:肯定是多个线程咯断点:线程停止下载的位置续传:线程从停止下载的位置上继续下载,直到完成任务为止。核心分析:断点:当前
- 传统方式克隆羊问题现在有一只羊 tom,姓名为: tom,年龄为:1,颜色为:白色,请编写程序创建和 tom羊属性完全相同的10只羊。传统方
- 创建AlertDialog的步骤:1、创建AlertDialog.Builder对象2、调用Builder对象的setTitle
- 对网页中各种不同格式的发布时间进行抽取,将发布时间以规整的“yyyy-MM-dd HH:mm:ss”格式表示出来,只能尽量追求精确,但是因为
- 前言设计模式在我看来更像是一种设计思维或设计思想,它就像《孙子兵法》一样,为你的项目工程提供方向,让你的项目工程更加健壮、灵活,延续生命力。
- 前言由于多核系统普遍存在,并发性编程的应用无疑比以往任何时候都要广泛。但并发性很难正确实现,用户需要借助新工具来使用它。很多基于 JVM 的
- 在写程序的时候,有时候可能需要设置小数的位数,那么java中有哪几种保留小数位数的方法呢?本文以两位小数为例给出四种方法。package C
- 最近项目上的一个上传文件功能,项目是MVC+EF+LigerUI 来做的,贴出来大家一起分享下1、页面需要引用这个JS 和 CSS<s
- 简介一个APP如果没有页面跳转那么是没有灵魂的,页面跳转的一个常用说法就是Navigator,flutter作为一个最为优秀的前端框架,Na
- 下面是函数定义: NTSTATUS RtlAdjustPrivilege ( ULONG Privilege, BOOLEAN Enable
- 1、为什么使用缓存  我们知道内存的读取速度远大于硬盘的读取速度。当需要重复地获取相同数据时,一次一次地
- 1.Java内存模型JAVA定义了一套在多线程读写共享数据时时,对数据的可见性、有序性和原子性的规则和保障。屏蔽掉不同操作系统间的微小差异。
- 一、简介 线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:ThreadPoolE
- 前言Android开发中经常使用findViewById来获取控件然后进行一些列操作,当控件太多的时候代码就非常臃肿,今天就来学习一个新的开
- 1.JSON数据交互1.1 JSON概述JSON 是一种轻量级的数据交换格式,是一种理想的数据交互语言,它易于阅读和编写,同时也易于机器解析
- 本文列举了几个方法: 1. 使用java.math.BigDecimal &n
- 本文实例讲述了Android编程实现点击EditText之外的控件隐藏软键盘功能。分享给大家供大家参考,具体如下:工具类...public
- 栈栈(stack)又名堆栈,它是一种运算受限的线性表 。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。
- 本文实例讲述了java内部类原理与用法。分享给大家供大家参考,具体如下:概念内部类:可以包含在另外一个类中的类外部类:包含内部类的类每个内部
- 前言这篇文章的内容基于对Spring Security 认证流程的理解,如果你不了解,可以读一下这篇文章:Spring Security 认