java中DelayQueue的使用
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
欢迎关注我的公众号:程序那些事,更多精彩等着您!
更多内容请访问 www.flydean.com
java中DelayQueue的使用的更多相关文章
- java中DelayQueue的一个使用陷阱分析
最近工作中有接触到DelayQueue,网上搜索资料的时候发现一篇文章谈到DelayQueue的坑.点击打开链接 文中已经总结了遇到坑的地方,还有解决方案.不过我第一眼看一下没弄明白为什么,所以翻了翻 ...
- 延时队列:Java中的DelayQueue
Java中的DelayQueue位于java.util.concurrent包下,本质是由PriorityQueue和BlockingQueue实现的阻塞优先级队列. 放入队列的元素需要实现java. ...
- Java中常用的七个阻塞队列第二篇DelayQueue源码介绍
Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...
- Java中的多线程你只要看这一篇就够了
学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:279558494 我们一起学Java! 引 如果对什么是线程.什么是进程仍存有疑惑, ...
- 聊聊并发(七)——Java中的阻塞队列
3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...
- [转]Java中的多线程你只要看这一篇就够了
如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...
- java中的各种Queue
java中的各种并发Queue可以归为以下的几种: ConcurrentLinkedQueue: 一个由链表结构组成的非阻塞队列 ArrayBlockingQueue :一个由数组结构组成的有界阻塞队 ...
- 一篇图看清Java中的各种Queue
说到数据结构,我们大概可以列出这么几个:数组,链表,栈,队列,集合,哈希表. 其中 队列 作为一个常用的数据结构,在Java中也有各种形式的实现. 顶级接口为java.util.queue. java ...
- Java 中的多线程你只要看这一篇就够了
引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...
- Java 中的队列 Queue
一.队列的定义 我们都知道队列(Queue)是一种先进先出(FIFO)的数据结构,Java中定义了java.util.Queue接口用来表示队列.Java中的Queue与List.Set属于同一个级别 ...
随机推荐
- python中操作csv
示例 import csv with open('t.csv', mode='r', encoding='utf-8') as f: reader_obj = csv.reader(f) # 通过re ...
- Kotlin 协程四 —— Flow 和 Channel 的应用
目录 一. Flow 与 Channel 的相互转换 1.1 Flow 转换为 Channel 1.1.1 ChannelFlow 1.1.2 produceIn -- 将 Flow 转换为单播式 C ...
- Django之第三方平台QQ授权登录的实现
接入指南:https://wiki.connect.qq.com/成为开发者 准备工作 成为开发者 首先要有一个开发者账号,https://connect.qq.com/ 登录后点击用户头像,修改个人 ...
- 【Azure 存储服务】关于中国区Azure Storage Account 存储账号服务误删除后的恢复问题
问题描述 在Azure上,如果需要恢复之前删除的存储账户(Storage Account), 有什么办法呢? 问题解答 Azure 现在推出了自主恢复已删除的存储账号的功能,具体步骤如下: 第一步: ...
- spark conf、config配置项总结
1.structured-streaming的state 配置项总结 Config Name Description Default Value spark.sql.streaming.stateSt ...
- Java 从键盘读入学生成绩 找出最高分 并输出学生等级成绩 * 成绩>=最高分-10 等级为’A‘ * 成绩>=最高分-20 等级为’B‘ * 成绩>=最高分-30 等级为'C' * 其余 等级为’D‘
1 /* 2 * 从键盘读入学生成绩 找出最高分 并输出学生等级成绩 3 * 成绩>=最高分-10 等级为'A' 4 * 成绩>=最高分-20 等级为'B' 5 * 成绩>=最高分- ...
- Java 基本数据类型之间的运算规则
1 /*** 2 * 基本数据类型之间的运算规则 3 * 4 * 前提:7中基本数据类型运算 5 * 6 * 1.自动类型提升: 7 * 当容量小的类型与容量大的数据类型的变量做运算时,结果自动提升为 ...
- MySql变量说明
1 #变量 2 /* 3 系统变量: 4 全局变量 5 会话变量 6 7 自定义变量: 8 用户变量 9 局部变量 10 11 */ 12 #一.系统变量 13 /* 14 说明:变量由系统定义,不是 ...
- JS4-BOM浏览器对象类型
什么是BOM 浏览器的顶级对象 页面加载事件以及注意事项 定时器函数 JS执行机制 页面跳转.刷新 history.navigator对象 什么是BOM 浏览器对象模型(Browser Object ...
- Nacos服务跨分组调用
一. 问题背景 nacos有两种服务隔离的机制,一个是空间namespace,一般我们用namespace区分环境,另外一个是分组group,nacos的默认调用机制是同namespace下的同gro ...