详解Java线程池队列中的延迟队列DelayQueue

 更新时间:2022年12月04日 15:39:23   作者:刨红薯的小羊竿尔  
这篇文章主要为大家详细介绍了Java线程池队列中的延迟队列DelayQueue的相关资料,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

在阻塞队里中,除了对元素进行增加和删除外,我们可以把元素的删除做一个延迟的处理,即使用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元素

到此这篇关于详解Java线程池队列中的延迟队列DelayQueue的文章就介绍到这了,更多相关Java延迟队列DelayQueue内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java如何使用httpclient检测url状态及链接是否能打开

    Java如何使用httpclient检测url状态及链接是否能打开

    这篇文章主要介绍了Java如何使用httpclient检测url状态及链接是否能打开,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java使用Apache compress实现文件夹压缩成Zip包

    Java使用Apache compress实现文件夹压缩成Zip包

    Apache common提供了很多实用的工具包,这篇文章主要来和大家介绍一下Java如何使用Apache compress包实现文件夹压缩成Zip包,希望对大家有所帮助
    2024-01-01
  • java递归法求字符串逆序

    java递归法求字符串逆序

    这篇文章主要介绍了java递归法求字符串逆序,涉及java递归调用的相关操作技巧,需要的朋友可以参考下
    2015-05-05
  • SpringBoot配置Redis自定义过期时间操作

    SpringBoot配置Redis自定义过期时间操作

    这篇文章主要介绍了SpringBoot配置Redis自定义过期时间操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • IntelliJ IDEA运行SpringBoot项目的详细步骤

    IntelliJ IDEA运行SpringBoot项目的详细步骤

    这篇文章主要介绍了IntelliJ IDEA如何运行SpringBoot项目,步骤一配置maven,步骤二配置JDK环境,紧接着通过步骤三检查数据库的配置,最后一步数据库连接,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • java运行错误A JNI error的解决方案

    java运行错误A JNI error的解决方案

    这篇文章主要介绍了java运行错误A JNI error的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 多层嵌套的json的值如何解析/替换

    多层嵌套的json的值如何解析/替换

    这篇文章主要介绍了多层嵌套的json的值如何解析/替换的方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Java实现重定向过程中添加请求头信息

    Java实现重定向过程中添加请求头信息

    在Java中,我们经常需要使用网络请求来与服务器进行通信,在进行网络请求时,有时我们需要在重定向过程中添加请求头信息,本文将介绍如何使用Java在重定向过程中添加请求头,并提供相应的代码示例,
    2023-10-10
  • 解析spring-security权限控制和校验的问题

    解析spring-security权限控制和校验的问题

    这篇文章主要介绍了解析spring-security权限控制和校验的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Spring Data JPA 整合QueryDSL的使用案例

    Spring Data JPA 整合QueryDSL的使用案例

    QueryDSL 是一个用于构建类型安全的 SQL 查询的 Java 库,它的主要目标是简化在 Java 中构建和执行 SQL 查询的过程,同时提供类型安全性和更好的编码体验,对Spring Data JPA 整合QueryDSL使用案例感兴趣的朋友跟随小编一起看看吧
    2023-08-08

最新评论