详解java中DelayQueue的使用

简介

今天给大家介绍一下DelayQueue,DelayQueue是BlockingQueue的一种,所以它是线程安全的,DelayQueue的特点就是插入Queue中的数据可以按照自定义的delay时间进行排序。只有delay时间小于0的元素才能够被取出。

DelayQueue

先看一下DelayQueue的定义:

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
  implements BlockingQueue<E>

从定义可以看到,DelayQueue中存入的对象都必须是Delayed的子类。

Delayed继承自Comparable,并且需要实现一个getDelay的方法。

为什么这样设计呢?

因为DelayQueue的底层存储是一个PriorityQueue,在之前的文章中我们讲过了,PriorityQueue是一个可排序的Queue,其中的元素必须实现Comparable方法。而getDelay方法则用来判断排序后的元素是否可以从Queue中取出。

DelayQueue的应用

DelayQueue一般用于生产者消费者模式,我们下面举一个具体的例子。

首先要使用DelayQueue,必须自定义一个Delayed对象:

@Data
public class DelayedUser implements Delayed {
  private String name;
  private long avaibleTime;

  public DelayedUser(String name, long delayTime){
    this.name=name;
    //avaibleTime = 当前时间+ delayTime
    this.avaibleTime=delayTime + System.currentTimeMillis();

  }

  @Override
  public long getDelay(TimeUnit unit) {
    //判断avaibleTime是否大于当前系统时间,并将结果转换成MILLISECONDS
    long diffTime= avaibleTime- System.currentTimeMillis();
    return unit.convert(diffTime,TimeUnit.MILLISECONDS);
  }

  @Override
  public int compareTo(Delayed o) {
    //compareTo用在DelayedUser的排序
    return (int)(this.avaibleTime - ((DelayedUser) o).getAvaibleTime());
  }
}

上面的对象中,我们需要实现getDelay和compareTo方法。

接下来我们创建一个生产者:

@Slf4j
@Data
@AllArgsConstructor
class DelayedQueueProducer implements Runnable {
  private DelayQueue<DelayedUser> delayQueue;

  private Integer messageCount;

  private long delayedTime;

  @Override
  public void run() {
    for (int i = 0; i < messageCount; i++) {
      try {
        DelayedUser delayedUser = new DelayedUser(
            new Random().nextInt(1000)+"", delayedTime);
        log.info("put delayedUser {}",delayedUser);
        delayQueue.put(delayedUser);
        Thread.sleep(500);
      } catch (InterruptedException e) {
        log.error(e.getMessage(),e);
      }
    }
  }
}

在生产者中,我们每隔0.5秒创建一个新的DelayedUser对象,并入Queue。

再创建一个消费者:

@Slf4j
@Data
@AllArgsConstructor
public class DelayedQueueConsumer implements Runnable {

  private DelayQueue<DelayedUser> delayQueue;

  private int messageCount;

  @Override
  public void run() {
    for (int i = 0; i < messageCount; i++) {
      try {
        DelayedUser element = delayQueue.take();
        log.info("take {}",element );
      } catch (InterruptedException e) {
        log.error(e.getMessage(),e);
      }
    }
  }
}

在消费者中,我们循环从queue中获取对象。

最后看一个调用的例子:

  @Test
  public void useDelayedQueue() throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    DelayQueue<DelayedUser> queue = new DelayQueue<>();
    int messageCount = 2;
    long delayTime = 500;
    DelayedQueueConsumer consumer = new DelayedQueueConsumer(
        queue, messageCount);
    DelayedQueueProducer producer = new DelayedQueueProducer(
        queue, messageCount, delayTime);

    // when
    executor.submit(producer);
    executor.submit(consumer);

    // then
    executor.awaitTermination(5, TimeUnit.SECONDS);
    executor.shutdown();

  }

上面的测试例子中,我们定义了两个线程的线程池,生产者产生两条消息,delayTime设置为0.5秒,也就是说0.5秒之后,插入的对象能够被获取到。

线程池在5秒之后会被关闭。

运行看下结果:

[pool-1-thread-1] INFO com.flydean.DelayedQueueProducer - put delayedUser DelayedUser(name=917, avaibleTime=1587623188389)
[pool-1-thread-2] INFO com.flydean.DelayedQueueConsumer - take DelayedUser(name=917, avaibleTime=1587623188389)
[pool-1-thread-1] INFO com.flydean.DelayedQueueProducer - put delayedUser DelayedUser(name=487, avaibleTime=1587623188899)
[pool-1-thread-2] INFO com.flydean.DelayedQueueConsumer - take DelayedUser(name=487, avaibleTime=1587623188899)

我们看到消息的put和take是交替进行的,符合我们的预期。

如果我们做下修改,将delayTime修改为50000,那么在线程池关闭之前插入的元素是不会过期的,也就是说消费者是无法获取到结果的。

总结

DelayQueue是一种有奇怪特性的BlockingQueue,可以在需要的时候使用。

本文的例子https://github.com/ddean2009/learn-java-collections

以上就是详解java中DelayQueue的使用的详细内容,更多关于java DelayQueue的资料请关注我们其它相关文章!

时间: 2020-10-19

详解SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)

引言 ​前两天在写一个实时数据处理的项目,项目要求是 1s 要处理掉 1k 的数据,这时候显然光靠查数据库是不行的,技术选型的时候老大跟我提了一下使用 Layering-Cache 这个开源项目来做缓存框架. ​之间问了一下身边的小伙伴,似乎对这块了解不多.一般也就用用 Redis 来缓存,应该是很少用多级缓存框架来专门性的管理缓存吧. ​趁着这个机会,我多了解了一些关于 SpringBoot 中缓存的相关技术,于是有了这篇文章! 在项目性能需求比较高时,就不能单单依赖数据库访问来获取数据了,必

Java sm3加密算法的实现

1.准备工作 所需jar包: bcprov-jdk15on-1.59.jar commons-lang3-3.1.jar 对应的maven依赖 <!--sm3,sm4加密算法--> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.66</version> <

Java多线程并发开发之DelayQueue使用示例

在学习Java 多线程并发开发过程中,了解到DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走.这种队列是有序的,即队头对象的延迟到期时间最长.注意:不能将null元素放置到这种队列中. Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象.此接口的实现必须定义一个 compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序. 在网上看到了一些

springboot执行延时任务之DelayQueue的使用详解

DelayQueue简介 DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素. 队列的头部,是延迟期满后保存时间最长的delay元素. 在很多场景我们需要用到延时任务,比如给客户异步转账操作超时后发通知告知用户,还有客户下单后多长时间内没支付则取消订单等等,这些都可以使用延时任务来实现. jdk中DelayQueue可以实现上述需求,顾名思义DelayQueue就是延时队列. DelayQueue提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素. 没有

为SpringBoot服务添加HTTPS证书的方法

HTTPS是HTTP的安全版本,旨在提供数据传输层安全性(TLS).当你的应用不使用HTTP协议的时候,浏览器地址栏就会出现一个不安全的提示.HTTPS加密每个数据包以安全方式进行传输,并保护敏感数据免受窃听者或黑客的攻击. 您可以通过在Web应用程序上安装SSL证书来实现HTTPS,互联网上受信任的证书通常是需要(CA)颁发的证书.为了学习目的,您也可以使用自签名证书,比如:使用Java Keytool生成自签名证书. 一.自签名证书 您可以使用位于JDK bin文件夹下的Keytool生成证

如何劫持Java应用的HTTP请求

背景 全链路追踪中,针对部分特殊的流量,希望将它引导到特定服务上(这个特定服务不在正常请求的链路上)--问题可以被抽象为解决进程间通信过程中目标进程的选择. 进程间通信方式很多,本篇只关注 Java 进程间套接字通信下 HTTP 形式的请求劫持,引导特定流量到特定进程. 解决方案 可行的处理方案繁多.自顶向下从应用.框架.JVM.Container Runtime.System Call.网络协议栈等级别,均可着手解决.侵入性最强的操作就是要求所有业务应用都主动实现 HTTP 请求分流逻辑:次一

Java Spring Controller 获取请求参数的几种方法详解

Java Spring Controller 获取请求参数的几种方法  1.直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交.若"Content-Type"="application/x-www-form-urlencoded",可用post提交 url形式:http://localhost:8080/SSMDemo/demo/addUser1?username=lixiaoxi&password=1

java实现HttpClient异步请求资源的方法

本文实例讲述了java实现HttpClient异步请求资源的方法.分享给大家供大家参考.具体实现方法如下: package demo; import java.util.concurrent.CountDownLatch; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.nio.client.DefaultHttpAsyn

java发送http get请求的两种方法(总结)

长话短说,废话不说 一.第一种方式,通过HttpClient方式,代码如下: public static String httpGet(String url, String charset) throws HttpException, IOException { String json = null; HttpGet httpGet = new HttpGet(); // 设置参数 try { httpGet.setURI(new URI(url)); } catch (URISyntaxExc

Java编程Post数据请求和接收代码详解

这两天在做http服务端请求操作,客户端post数据到服务端后,服务端通过request.getParameter()进行请求,无法读取到数据,搜索了一下发现是因为设置为text/plain模式才导致读取不到数据 urlConn.setRequestProperty("Content-Type","text/plain; charset=utf-8"); 若设置为以下方式,则通过request.getParameter()可以读取到数据 urlConn.setReq

java后台发起get请求获取响应数据

本文实例为大家分享了java后台发起get请求获取响应数据,供大家参考,具体内容如下 学习记录: 话不多说直接上代码: package com.jl.chromeTest; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java

java模拟多线程http请求代码分享

java模拟http发送请求,第一种是HttpURLConnection发送post请求,第二种是使用httpclient模拟post请求, 实例代码: package test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Exec

java解析出url请求的路径和参数键值对类(解析出url请求的路径,包括页面)

复制代码 代码如下: package RequestPackage; import java.util.HashMap; import java.util.Map; public class CRequest { /** * 解析出url请求的路径,包括页面 * @param strURL url地址 * @return url路径 */ public static String UrlPage(String strURL) { String strPage=null; String[] arr

java并发访问重复请求过滤问题

问题描述 前段时间遇到个问题,自己内部系统调用出现重复请求导致数据混乱. 发生条件:接受到一个请求,该请求没有执行完成又接受到相同请求,导致数据错误(如果是前一个请求执行完成,马上又接受相同请求不会有问题) 问题分析:是由于数据库的脏读导致 问题解决思路 1.加一把大大的锁 (是最简单的实现方式,但是性能堪忧,而且会阻塞请求) 2.实现请求拦截 (可以共用,但是怎么去实现却是一个问题,怎么用一个优雅的方式实现,并且方便复用) 3.修改实现 (会对原有代码做改动,存在风险,最主要的是不能共用) 最

Java后台判断ajax请求及处理过程详解

一.问题描述: 当访问一个需要登录的页面时,会有过滤器或者拦截器进行过滤拦截,如果用户没有登录,则跳转到登录页面. 当用户已经登录进入系统后,然后长时间没操作,等到session过期后,再点击一个ajax请求操作时,这时再跳转到登录页面就不合适了,因为这是ajax操作,拦截后跳到页面返回的结果js识别不了. 二.解决方法: 在过滤器或者拦截器上做识别,针对页面跳转请求和ajax请求分别处理. 页面跳转的不再详说,现在说的是ajax请求. 直接贴代码: import java.io.IOExcep