Java并发基础:DelayQueue全面解析!

2024-02-14 10:36

本文主要是介绍Java并发基础:DelayQueue全面解析!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java并发基础:DelayQueue全面解析! - 程序员古德

内容概要

DelayQueue类专为处理延迟任务设计,它允许开发者将任务与指定的延迟时间关联,并在任务到期时自动处理,从而避免了不必要的轮询和资源浪费,此外,DelayQueue内部基于优先队列实现,确保最先到期的任务总是优先被处理,使得任务调度更为高效和精准。

核心概念

DelayQueue 实现了一个支持延时获取元素的无界阻塞队列,只有当其指定的延迟时间到达时,队列中的元素才能被获取,这种队列非常有用,当需要在一段指定的延迟之后处理任务或者数据时,DelayQueue 就派上了用场。

举一个实际生活中的业务来说明 DelayQueue 的使用场景,如下:

假设有一个在线订餐系统,当客户下单后,餐厅需要一段时间来准备食物,这段时间就是延迟,一旦食物准备好了,系统就会通知配送员进行配送,在这个场景中可以使用 DelayQueue 来简单的实现,如下步骤:

  1. 当客户下单时,可以创建一个包含订单信息和预计准备完成时间的 Delayed 对象,并将其放入 DelayQueue 中。
  2. Delayed 对象会根据其预计准备完成时间进行排序,最早完成的订单会被放在队列的前面。
  3. 系统可以有一个后台线程不断地从 DelayQueue 中尝试获取订单,如果订单的预计准备时间还没有到,DelayQueue 会阻塞这个线程的获取操作。
  4. 一旦有订单的预计准备时间到达(即订单已经准备好了),后台线程就可以从 DelayQueue 中获取到这个订单,并通知配送员进行配送。

DelayQueue它是一种无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走,这种队列是有序的,即队头对象的延迟到期的时间最长,如果没有任何延迟到期,那么就不会有任何头元素,并且poll将返回null(当然,这取决于具体的实现,有可能抛出异常),DelayQueue通常用于解决以下类似的问题,如下:

  1. 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了,可以删除或者更新缓存。
  2. 定时任务调度:系统需要定时执行的任务可以交给DelayQueue保存,然后使用一个线程循环查询DelayQueue,一旦获取到任务就开始执行,比如每个一段时间发送一封邮件、扫描一次日志文件等。
  3. 订单超时处理:在电商系统中,用户提交了一个订单,就可以把这个订单放进DelayQueue,设置超时时间,超时后仍然没有支付就取消订单。

代码案例

以下是一个使用DelayQueue的示例,定义了一个DelayedTask类来实现Delayed接口,表示一个带有延迟时间的任务,然后在主程序中,,创建了一个DelayQueue来存储这些任务,并在单独的线程中处理这些任务,如下代码:

import java.util.concurrent.BlockingQueue;  
import java.util.concurrent.DelayQueue;  
import java.util.concurrent.Delayed;  
import java.util.concurrent.TimeUnit;  // 定义一个带有延迟时间的任务  
class DelayedTask implements Delayed {  private final long startTime;  // 任务开始时间  private final String taskName;  // 任务名称  public DelayedTask(long delay, String taskName) {  this.startTime = System.currentTimeMillis() + delay;  this.taskName = taskName;  }  @Override  public long getDelay(TimeUnit unit) {  long diff = startTime - System.currentTimeMillis();  return unit.convert(diff, TimeUnit.MILLISECONDS);  }  @Override  public int compareTo(Delayed o) {  if (this.startTime < ((DelayedTask) o).startTime) {  return -1;  }  if (this.startTime > ((DelayedTask) o).startTime) {  return 1;  }  return 0;  }  @Override  public String toString() {  return "Task " + taskName + " at " + startTime;  }  
}  // 定义处理任务的线程  
class TaskHandler implements Runnable {  private final BlockingQueue<DelayedTask> queue;  public TaskHandler(BlockingQueue<DelayedTask> queue) {  this.queue = queue;  }  @Override  public void run() {  while (true) {  try {  // 从队列中取出到期的任务进行处理  DelayedTask task = queue.take();  System.out.println("Handled: " + task);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  
}  public class DelayQueueExample {  public static void main(String[] args) {  // 创建一个DelayQueue来存储任务  BlockingQueue<DelayedTask> queue = new DelayQueue<>();  // 向队列中添加任务,每个任务都有一个延迟时间(以毫秒为单位)  try {  queue.put(new DelayedTask(3000, "Task1"));  queue.put(new DelayedTask(5000, "Task2"));  queue.put(new DelayedTask(1000, "Task3"));  } catch (InterruptedException e) {  e.printStackTrace();  }  // 创建一个线程来处理任务  Thread handler = new Thread(new TaskHandler(queue));  handler.start();  }  
}

核心API

DelayQueue是一个支持延时获取元素的无界阻塞队列,只有当其元素的延迟到期时,才能从队列中获取到该元素,以下是DelayQueue类中一些主要方法的含义,如下:

  1. offer(E e)
    将指定的元素插入此延迟队列中,如果成功,则返回true;如果当前没有可用的空间,则返回false(实际上,由于DelayQueue是无界的,所以理论上这个方法总是返回true,除非参数为null)。
  2. put(E e) throws InterruptedException
    将指定的元素插入此延迟队列中,等待必要的时间来让空间变得可用(实际上,由于DelayQueue是无界的,这个方法将立即返回)。
  3. take() throws InterruptedException
    检索并移除此队列的头部,等待如果必要直到一个元素变得可用,此方法返回队列头部的元素,并将其从队列中移除。
  4. poll(long timeout, TimeUnit unit) throws InterruptedException
    检索并移除此队列的头部,等待指定的时间以使元素变得可用,如果有必要的话,如果在指定的时间内队列仍然是空的或者没有元素到期,则返回null
  5. peek()
    检索,但不移除此队列的头部。如果此队列为空或者所有元素的延迟都未到期,则返回null
  6. drainTo(Collection<? super E> c)
    移除此队列中所有可用的元素,并将它们添加到给定的集合中,尝试添加元素到集合c时可能会由于容量限制而失败,并且可能导致在抛出异常时元素同时存在于两个集合中,或者既不在原来的队列中,也不在集合c中。
  7. drainTo(Collection<? super E> c, int maxElements)
    移除此队列中最多给定数量的可用元素,并将它们添加到给定的集合中。
  8. remove(Object o)
    从此队列中移除单个实例(如果存在)的指定元素,更确切地说,如果此队列包含一个或多个满足(o==null ? e==null : o.equals(e))的元素e,则移除并返回队列中的一个这样的元素。如果队列为空或者不包含该元素,则返回false
  9. size()
    返回队列中的元素数量,这个值是一个近似值,因为在此方法的调用和返回之间,队列中的元素数量可能已经发生了变化。
  10. remainingCapacity()
    始终返回Integer.MAX_VALUE,因为DelayQueue是无界的。
  11. clear()
    从此队列中移除所有元素。
  12. contains(Object o)
    如果此队列包含指定的元素,则返回true,更确切地说,当且仅当此队列至少包含一个满足(o==null ? e==null : o.equals(e))的元素e时,才返回true
  13. iterator()
    返回在此队列中的元素上进行迭代的迭代器,因为队列是并发的,所以迭代器本身是弱一致性的。它最多只能反映出在迭代器创建时队列的某个状态。
  14. toArray()
    返回以适当顺序包含此队列中所有元素的数组。
  15. toArray(T[] a)
    返回以适当顺序包含此队列中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

需要注意的是,由于DelayQueue是一个并发集合,因此在多线程环境中使用时,其状态可能会随时发生变化,此外,为了保证队列的正确性,使用DelayQueue的元素必须实现Delayed接口,并提供适当的getDelaycompareTo方法实现。

核心总结

Java并发基础:DelayQueue全面解析! - 程序员古德

DelayQueue类是一种无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走,DelayQueue非常适合用于设计缓存系统、任务调度等场景。

它的优点在于可以方便地实现延迟操作,避免了频繁的轮询,提高了程序的运行效率,此外,DelayQueue内部使用优先队列(PriorityQueue)来保证最小延迟的元素总是优先出队,这使得任务调度更为精准。

它的缺点在于,由于是无界队列,如果不当使用,可能会导致内存溢出,另外,在多线程环境下,虽然DelayQueue是线程安全的,但如果不注意元素的唯一性和线程间的协作,可能会引发问题。

关注我,每天学习互联网编程技术 - 程序员古德

END!

往期回顾

Java并发基础:LinkedBlockingDeque全面解析!

Java并发基础:LinkedTransferQueue全面解析!

Java并发基础:LinkedBlockingQueue全面解析!

Java并发基础:Deque接口和Queue接口的区别?

Java并发基础:FutureTask全面解析!

这篇关于Java并发基础:DelayQueue全面解析!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/708272

相关文章

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

python常见环境管理工具超全解析

《python常见环境管理工具超全解析》在Python开发中,管理多个项目及其依赖项通常是一个挑战,下面:本文主要介绍python常见环境管理工具的相关资料,文中通过代码介绍的非常详细,需要的朋友... 目录1. conda2. pip3. uvuv 工具自动创建和管理环境的特点4. setup.py5.

全面解析HTML5中Checkbox标签

《全面解析HTML5中Checkbox标签》Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中C... 在html5中,Checkbox(复选框)是一种常用的表单元素,允许用户在一组选项中选择多个项目。本

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关