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

标签:Java,延迟队列,DelayQueue
0
投稿

猜你喜欢

  • Java简单验证身份证功能示例

    2023-08-15 23:36:32
  • 详解Java Proxy动态 代理机制

    2023-07-24 21:01:58
  • JAVA实现账户取款和存款操作

    2023-08-22 16:31:45
  • Java线程状态变换过程代码解析

    2023-08-06 18:05:29
  • springboot 使用自定义的aspect的示例代码

    2023-08-06 08:55:14
  • Vs2022环境下安装低版本.net framework的实现步骤

    2023-07-04 02:58:12
  • Flutter Widgets粘合剂CustomScrollView NestedScrollView滚动控件

    2023-07-06 01:24:29
  • Android RecyclerView基本使用详解

    2023-07-24 21:13:30
  • AsyncTask官方文档教程整理

    2023-07-31 20:25:08
  • 六款值得推荐的android(安卓)开源框架简介

    2023-06-24 01:46:54
  • MyBatis配置的应用与对比jdbc的优势

    2023-08-27 07:03:47
  • Spring @Conditional通过条件控制bean注册过程

    2023-08-06 10:00:11
  • 如何在IDE部署springboot项目(有swagger和无swagger都是一样的)到服务器或者虚拟机上的docker

    2023-09-01 00:33:25
  • Android Camera+SurfaceView自动聚焦防止变形拉伸

    2023-06-18 06:35:54
  • C#异步编程Task的创建方式

    2023-07-23 06:22:43
  • java8到java15的新功能简介

    2023-07-28 02:18:18
  • Java 根据网址查询DNS/IP地址的方法

    2023-06-21 15:31:54
  • Dubbo实现分布式日志链路追踪

    2023-08-23 21:00:54
  • 一文搞懂MyBatis多数据源Starter实现

    2023-07-19 03:34:22
  • Android如何让WebView中的HTML5页面实现视频全屏播放

    2023-07-29 00:32:06
  • asp之家 软件编程 m.aspxhome.com