java并发中DelayQueue延迟队列原理剖析

Java 并发中 DelayQueue 延迟队列原理剖析

DelayQueue 是 Java 并发包中提供的一种特殊队列,它能够在一定的时间内延迟一些操作的执行。下面就来深入了解一下 DelayQueue 的原理。

DelayQueue 的基本特点

DelayQueue 继承自 java.util.concurrent.Delayed 接口,它的元素必须要实现该接口以表明在队列中的延迟过期时间以及当元素到达过期时间后应该怎样处理。DelayQueue 的一些基本特点如下:

  • 允许 null 元素。
  • 延迟元素必须实现 Delayed 接口,其中判断是否到期的方法是 getDelay(),其返回值表示过期时间与当前时间的差值。
  • 如果队列中没有元素到期,那么就会一直阻塞等待,这样可以保证队列中先到期的元素先被处理。

DelayQueue 的源码分析

下面是 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 中的元素个数,实际上就是 q 的 size()
    private int size;

    // 把一个元素放到 DelayQueue 中,这个方法会返回 false,因为 put() 永远不会阻塞
    public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            q.offer(e);                         // 将元素插入到优先队列 q 中
            if (q.peek() == e) {                // 如果新插入的元素变成了队首,那么需要唤醒可能正在等待的线程
                leader = null;
                available.signal();
            }
            size++;
            return true;
        } finally {
            lock.unlock();
        }
    }

    // 获取队首元素,但是如果队首没有到期,那么该方法会阻塞直到超时
    private E peekExpired() {
        // 获取队首元素,但不弹出
        E first = q.peek();
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
            return first;
    }

    // 从 DelayQueue 中弹出一个元素,如果没有到期,那么返回 null
    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = peekExpired();            // 获取队首元素,但是如果队首没有到期,那么该方法会阻塞直到超时
            if (first == null)
                return null;
            E x = q.poll();                     // 弹出队首元素
            assert x == first;
            size--;
            return x;
        } finally {
            lock.unlock();                      // 释放锁
        }
    }
}

从上面的源码可知,DelayQueue 内部数据结构是基于 PriorityQueue 实现的。因为 PriorityQueue 是一种优先队列,是一种能够自动排序的队列,并且重要的元素都放在队首,而 DelayQueue 的每个元素都有一个过期时间,所以将 DelayQueue 实现为基于 PriorityQueue 的高级队列是较为合理的。

下面给出一段使用 DelayQueue 的基本示例代码:

public class DelayQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Message> delayQueue = new DelayQueue<>();
        delayQueue.put(new Message("Message ID 1", 5000L));
        delayQueue.put(new Message("Message ID 2", 1000L));
        delayQueue.put(new Message("Message ID 3", 2000L));
        delayQueue.put(new Message("Message ID 4", 1500L));

        // 模拟实时接收消息并处理
        while (!delayQueue.isEmpty()) {
            Message message = delayQueue.take();
            System.out.println(new Date() + ": " + message);
        }
    }
}

// 该类是 DelayQueue 中元素的实体类,必须实现 Delayed 接口
class Message implements Delayed {
    private String id;
    private Long delayTime;     // 延迟到期时间

    public Message(String id, Long delayTime) {
        this.id = id;
        this.delayTime = System.currentTimeMillis() + delayTime;
    }

    // 返回值是元素的剩余延迟时间,当返回值小于等于 0 时,表示到期,可以从队列中取出
    @Override
    public long getDelay(TimeUnit unit) {
        return delayTime - System.currentTimeMillis();
    }

    // 用于计数器才有的,作用是更快的比较两个元素大小,如果返回 0,则表示两个元素相等
    @Override
    public int compareTo(Delayed o) {
        return this.getDelay(TimeUnit.SECONDS) > o.getDelay(TimeUnit.SECONDS) ? 1 : -1;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id='" + id + '\'' +
                ", delayTime=" + delayTime +
                '}';
    }
}

上面的示例代码就是一个简单的基于 DelayQueue 的消息队列。在这个示例中,我们使用了 DelayQueue 来传递一些消息,但是每个消息到达后会被指定一个延迟时间,也就是在延迟时间之后才能处理该消息。在 while 循环内部使用 delayQueue.take() 方法从队列中取出一个元素,该方法会阻塞等待,直到队列中有元素到期。在控制台输出中可以看到每个元素出队列的时间以及它的 ID。

同时,内部也为 DelayQueue 中元素的实体类提供了一个基本示例,该类必须实现 Delayed 接口,并可以通过实现该接口中的两个方法分别来规定每个元素的延迟时间和具体的处理逻辑。

以上就是 Java 并发中 DelayQueue 延迟队列原理剖析的核心内容。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java并发中DelayQueue延迟队列原理剖析 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 并发编程ConcurrentLinkedQueue示例详解

    下面是“并发编程ConcurrentLinkedQueue示例详解”的完整攻略: 并发编程ConcurrentLinkedQueue示例详解 什么是ConcurrentLinkedQueue ConcurrentLinkedQueue是Java的一个并发工具类,它提供了线程安全的队列操作。与LinkedList不同,ConcurrentLinkedQueue…

    多线程 2023年5月16日
    00
  • 理论讲解python多进程并发编程

    理论讲解Python多进程并发编程 什么是多进程并发编程 多进程并发编程指的是在同一时间内,有多个进程同时运行,从而达到提高程序执行效率的目的。这种编程方式可以更好的利用多核CPU的能力,提高程序的计算能力和并发性。 如何实现多进程并发编程 Python提供了许多库来实现多进程并发编程,其中最常用的是multiprocessing库。在使用multiproc…

    多线程 2023年5月16日
    00
  • Java使用JMeter进行高并发测试

    针对“Java使用JMeter进行高并发测试”的完整攻略,我给您提供以下的步骤: 步骤一:安装JMeter 在进行JMeter进行高并发测试之前,确保您已经安装了最新版的JMeter,并全面理解测试的基本理念。 步骤二:编写测试计划 在JMeter中,测试计划是用于组织所有测试元素的根元素。在编写测试计划时,请确保包括以下内容:- 负载发生器:它是我们需要检…

    多线程 2023年5月16日
    00
  • 如何用PHP实现多线程编程

    创建多线程程序可以增加应用程序的效率,对于 PHP 程序员来说,也要掌握多线程编程技术。 实现 PHP 多线程编程的方式有很多,比如使用 pthreads 扩展、使用 pcntl 扩展、使用多进程(fork)等。下面我们举两个例子分别介绍使用 pthreads 扩展和多进程实现多线程编程的方法。 使用 pthreads 扩展 pthreads 扩展是一个多线…

    多线程 2023年5月17日
    00
  • Java并发编程示例(一):线程的创建和执行

    Java并发编程示例(一):线程的创建和执行 前言 Java是一门支持多线程编程的语言,多线程编程可以有效地提高程序的执行效率,特别是在涉及到网络编程、I/O操作以及复杂的计算任务时。本篇文章将会介绍Java中如何创建线程以及如何执行线程。 Java中的线程 Java中的线程是通过Thread类来实现的。在Java中创建线程有两种方式:继承Thread类和实…

    多线程 2023年5月17日
    00
  • python编程使用协程并发的优缺点

    Python编程使用协程并发的优缺点 什么是协程并发 “协程并发”指同时执行多个协程,在这些协程之间切换执行,实现并发的效果。这种并发实现方式相对于线程和进程有很大的优势,可以提高系统性能,减少资源占用。 协程并发的优点 更高的执行效率 协程并发能够减少系统资源的消耗,因此可以实现更高的执行效率。相对于线程或者进程,协程在切换时不需要进行上下文的切换,因此执…

    多线程 2023年5月16日
    00
  • 利用mysql事务特性实现并发安全的自增ID示例

    下面是利用MySQL事务特性实现并发安全的自增ID示例的完整攻略: 什么是自增ID 自增ID又称自增长ID或自增主键,指的是在数据库表中某一列的值在每次插入新数据时自动加1,以保证每条数据的主键唯一性。 在MySQL中,通常通过设置字段为INT或BIGINT类型,并将其设置为自动增加实现该功能。简单来说,就是通过自增ID来维护表中记录的唯一标识符。 什么是M…

    多线程 2023年5月17日
    00
  • java并发编程专题(一)—-线程基础知识

    让我来详细讲解“Java并发编程专题(一)—-线程基础知识”的完整攻略。 一、为什么要学习线程基础知识? 线程是程序并发执行的最小单位。在多核CPU的情况下,线程可以充分利用CPU的资源,提高程序的执行速度。 Java作为一种面向对象编程语言,线程是Java中最基本的类之一。学习线程基础知识,有助于掌握Java的基本语法和面向对象编程思想。 现代软件开发…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部