queue double java_一文弄懂java中的Queue家族

java中Queue家族简介

简介

java中Collection集合有三大家族List,Set和Queue。当然Map也算是一种集合类,但Map并不继承Collection接口。

List,Set在我们的工作中会经常使用,通常用来存储结果数据,而Queue由于它的特殊性,通常用在生产者消费者模式中。

现在很火的消息中间件比如:Rabbit MQ等都是Queue这种数据结构的展开。

今天这篇文章将带大家进入Queue家族。

Queue接口

先看下Queue的继承关系和其中定义的方法:

7a8b26e14606721899f9c5b7c131240c.png

Queue继承自Collection,Collection继承自Iterable。

Queue有三类主要的方法,我们用个表格来看一下他们的区别:

方法类型

方法名称

方法名称

区别

Insert

add

offer

两个方法都表示向Queue中添加某个元素,不同之处在于添加失败的情况,add只会返回true,如果添加失败,会抛出异常。offer在添加失败的时候会返回false。所以对那些有固定长度的Queue,优先使用offer方法。

Remove

remove

poll

如果Queue是空的情况下,remove会抛出异常,而poll会返回null。

Examine

element

peek

获取Queue头部的元素,但不从Queue中删除。两者的区别还是在于Queue为空的情况下,element会抛出异常,而peek返回null。

注意,因为对poll和peek来说null是有特殊含义的,所以一般来说Queue中禁止插入null,但是在实现中还是有一些类允许插入null比如LinkedList。

尽管如此,我们在使用中还是要避免插入null元素。

Queue的分类

一般来说Queue可以分为BlockingQueue,Deque和TransferQueue三种。

BlockingQueue

BlockingQueue是Queue的一种实现,它提供了两种额外的功能:

当当前Queue是空的时候,从BlockingQueue中获取元素的操作会被阻塞。

当当前Queue达到最大容量的时候,插入BlockingQueue的操作会被阻塞。

BlockingQueue的操作可以分为下面四类:

操作类型

Throws exception

Special value

Blocks

Times out

Insert

add(e)

offer(e)

put(e)

offer(e, time, unit)

Remove

remove()

poll()

take()

poll(time, unit)

Examine

element()

peek()

not applicable

not applicable

第一类是会抛出异常的操作,当遇到插入失败,队列为空的时候抛出异常。

第二类是不会抛出异常的操作。

第三类是会Block的操作。当Queue为空或者达到最大容量的时候。

第四类是time out的操作,在给定的时间里会Block,超时会直接返回。

BlockingQueue是线程安全的Queue,可以在生产者消费者模式的多线程中使用,如下所示:

class Producer implements Runnable {

private final BlockingQueue queue;

Producer(BlockingQueue q) { queue = q; }

public void run() {

try {

while (true) { queue.put(produce()); }

} catch (InterruptedException ex) { ... handle ...}

}

Object produce() { ... }

}

class Consumer implements Runnable {

private final BlockingQueue queue;

Consumer(BlockingQueue q) { queue = q; }

public void run() {

try {

while (true) { consume(queue.take()); }

} catch (InterruptedException ex) { ... handle ...}

}

void consume(Object x) { ... }

}

class Setup {

void main() {

BlockingQueue q = new SomeQueueImplementation();

Producer p = new Producer(q);

Consumer c1 = new Consumer(q);

Consumer c2 = new Consumer(q);

new Thread(p).start();

new Thread(c1).start();

new Thread(c2).start();

}

}

最后,在一个线程中向BlockQueue中插入元素之前的操作happens-before另外一个线程中从BlockQueue中删除或者获取的操作。

Deque

Deque是Queue的子类,它代表double ended queue,也就是说可以从Queue的头部或者尾部插入和删除元素。

同样的,我们也可以将Deque的方法用下面的表格来表示,Deque的方法可以分为对头部的操作和对尾部的操作:

方法类型

Throws exception

Special value

Throws exception

Special value

Insert

addFirst(e)

offerFirst(e)

addLast(e)

offerLast(e)

Remove

removeFirst()

pollFirst()

removeLast()

pollLast()

Examine

getFirst()

peekFirst()

getLast()

peekLast()

和Queue的方法描述基本一致,这里就不多讲了。

当Deque以 FIFO (First-In-First-Out)的方法处理元素的时候,Deque就相当于一个Queue。

当Deque以LIFO (Last-In-First-Out)的方式处理元素的时候,Deque就相当于一个Stack。

TransferQueue

TransferQueue继承自BlockingQueue,为什么叫Transfer呢?因为TransferQueue提供了一个transfer的方法,生产者可以调用这个transfer方法,从而等待消费者调用take或者poll方法从Queue中拿取数据。

还提供了非阻塞和timeout版本的tryTransfer方法以供使用。

我们举个TransferQueue实现的生产者消费者的问题。

先定义一个生产者:

@Slf4j

@Data

@AllArgsConstructor

class Producer implements Runnable {

private TransferQueue transferQueue;

private String name;

private Integer messageCount;

public static final AtomicInteger messageProduced = new AtomicInteger();

@Override

public void run() {

for (int i = 0; i < messageCount; i++) {

try {

boolean added = transferQueue.tryTransfer( "第"+i+"个", 2000, TimeUnit.MILLISECONDS);

log.info("transfered {} 是否成功: {}","第"+i+"个",added);

if(added){

messageProduced.incrementAndGet();

}

} catch (InterruptedException e) {

log.error(e.getMessage(),e);

}

}

log.info("total transfered {}",messageProduced.get());

}

}

在生产者的run方法中,我们调用了tryTransfer方法,等待2秒钟,如果没成功则直接返回。

再定义一个消费者:

@Slf4j

@Data

@AllArgsConstructor

public class Consumer implements Runnable {

private TransferQueue transferQueue;

private String name;

private int messageCount;

public static final AtomicInteger messageConsumed = new AtomicInteger();

@Override

public void run() {

for (int i = 0; i < messageCount; i++) {

try {

String element = transferQueue.take();

log.info("take {}",element );

messageConsumed.incrementAndGet();

Thread.sleep(500);

} catch (InterruptedException e) {

log.error(e.getMessage(),e);

}

}

log.info("total consumed {}",messageConsumed.get());

}

}

在run方法中,调用了transferQueue.take方法来取消息。

下面先看一下一个生产者,零个消费者的情况:

@Test

public void testOneProduceZeroConsumer() throws InterruptedException {

TransferQueue transferQueue = new LinkedTransferQueue<>();

ExecutorService exService = Executors.newFixedThreadPool(10);

Producer producer = new Producer(transferQueue, "ProducerOne", 5);

exService.execute(producer);

exService.awaitTermination(50000, TimeUnit.MILLISECONDS);

exService.shutdown();

}

输出结果:

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第0个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第1个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第2个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第3个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第4个 是否成功: false

[pool-1-thread-1] INFO com.flydean.Producer - total transfered 0

可以看到,因为没有消费者,所以消息并没有发送成功。

再看下一个有消费者的情况:

@Test

public void testOneProduceOneConsumer() throws InterruptedException {

TransferQueue transferQueue = new LinkedTransferQueue<>();

ExecutorService exService = Executors.newFixedThreadPool(10);

Producer producer = new Producer(transferQueue, "ProducerOne", 2);

Consumer consumer = new Consumer(transferQueue, "ConsumerOne", 2);

exService.execute(producer);

exService.execute(consumer);

exService.awaitTermination(50000, TimeUnit.MILLISECONDS);

exService.shutdown();

}

输出结果:

[pool-1-thread-2] INFO com.flydean.Consumer - take 第0个

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第0个 是否成功: true

[pool-1-thread-2] INFO com.flydean.Consumer - take 第1个

[pool-1-thread-1] INFO com.flydean.Producer - transfered 第1个 是否成功: true

[pool-1-thread-1] INFO com.flydean.Producer - total transfered 2

[pool-1-thread-2] INFO com.flydean.Consumer - total consumed 2

可以看到Producer和Consumer是一个一个来生产和消费的。

总结

本文介绍了Queue接口和它的三大分类,这三大分类又有非常多的实现类,我们将会在后面的文章中再详细介绍。

欢迎关注我的公众号:程序那些事,更多精彩等着您!

更多内容请访问 www.flydean.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/561088.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

semantic ui中文文档_Vuetify-广受欢迎的Material风格的开源UI框架

全世界范围内广受欢迎的 Vue UI 框架&#xff0c;一个非常精致的 Material Design UI 套件。 Material Design 风格 UI 框架Vuetify 是一个基于 Vue.js 精心打造 UI 组件库&#xff0c;整套 UI 设计为 Material 风格。能够让没有任何设计技能的开发者创造出时尚的 Material 风格…

提示tun虚拟网卡没有安装_Win10家庭版通过Hyper-V安装Centos7+Python3.7过程总结

Win10专业版自带有虚拟机Hyper-V, 只需要在控制面板--程序中将其添加到应用就可使用&#xff0c;非常方便&#xff0c;但我电脑预装的是Win10家庭版&#xff0c;没有这个工具&#xff0c;但可以通过以下方法把它安装上&#xff1a;新建文件Hyper-V.cmd&#xff0c;文件内容&…

nginx 带宽_谈谈Nginx和LVS各自的优缺点以及使用

在最开始呢&#xff0c;咱们先说一下什么叫负载均衡&#xff0c;负载均衡呢&#xff0c;就是将一批请求&#xff0c;根据请求的内容&#xff0c;分发到不同的后端去进行相应的处理&#xff0c;从而提供负载分担&#xff0c;主备切换等功能。对于不同的负载均衡软件&#xff0c;…

wordpress 自定义分类url 重写_WordPress导航主题-WebStack导航主题

8月份写了一个导航主题&#xff0c;陆陆续续更新了十几版&#xff0c;功能自认已经很完善了&#xff0c;知乎也注册很久了&#xff0c;在这水片文章。首页截图预览地址一为忆 - 收集国内外优秀设计网站、UI设计资源网站、灵感创意网站、素材资源网站&#xff0c;定时更新分享优…

sql跨表查询_白话django之ORM的查询语句

教程源码&#xff1a;z991/django_turital在日常开发中&#xff0c;数据库的增删改查&#xff08;CDUR&#xff09;中&#xff0c;查询需求偏多&#xff0c;所以查询的语法比增删改操作多得多&#xff0c;尤其是跨表关联查询&#xff0c;可以让代码精简很多年。直接上代码吧&am…

看不懂论文代码怎么办_学位论文中的公式排版(制表位+mathtype+域)

写在前面为什么把公式排版单独拉出来写一篇文章呢&#xff1f;因为公式排版实在是太难了。公式居中标号右对齐&#xff0c;简直反人类好么。在学校期间一直寻找方便的公式排版自动编号方法&#xff0c;但搜索出来的大多只是用到了制表位&#xff0c;公式标号还要自己敲。最后毕…

利用trunk实现vlan内通信_实现不同VLAN间通信——单臂路由

单臂路由是一种实现不同VLAN间通信的技术&#xff0c;其方法是在虚拟机内接口上配置虚拟子接口。该技术是一种应急技术&#xff0c;仅仅用于网络预算不足&#xff0c;无力购买三层交换机&#xff0c;或者网络规模很小的情况下使用。一、实验路由器只需要一个端口和交换机(二层)…

java dispatchevent_java事件处理机制

java中的事件机制的参与者有3种角色&#xff1a;1.event object&#xff1a;就是事件产生时具体的“事件”&#xff0c;用于listener的相应的方法之中&#xff0c;作为参数&#xff0c;一般存在与listerner的方法之中2.event source&#xff1a;具体的接受事件的实体&#xff0…

java中的action是指什么_Struts2【开发Action】知识要点

前言前面Struts博文基本把Struts的配置信息讲解完了.....本博文主要讲解Struts对数据的处理Action开发的三种方式在第一次我们写开发步骤的时候&#xff0c;我们写的Action是继承着ActionSupport类的...为啥我们继承了ActionSupport类呢&#xff1f;下面我就会讲解到继承Action…

hql 查询条件 set集合_Redis从入门到深入-Sorted_set的value

1. sorted_set 类型新的存储需求&#xff0c;数据排序有利于数据的有效展示&#xff0c;需要提供一种可以根据自身特征进行排序的方式需要的存储结构&#xff1a;新的存储模型&#xff0c;可以保存可排序的数据sorted_set类型&#xff1a;在set的存储结构基础上添加可排序字段2…

pdm生成java_PowerDesigner通过SQL语句生成PDM文件并将name和comment进行互相转换

本篇文章主要介绍了PowerDesigner通过SQL语句生成PDM文件并将name和comment进行互相转换 超详细过程(图文)&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下1.软件准备软件&#xff1a;Navicat 11.1&#xff0c;Powerdesigner 152.安装步骤第一步&…

java里面如何加入高级的东西_如何成为一名Java高级架构师

近些年来互联网快速发展&#xff0c;现阶段的数据量和高并发的诉求&#xff0c;引起了不少传统的技术人员的力不从心&#xff0c;企业愈发关注到了系统架构的重要性&#xff0c;既需要掌控整体又需要洞悉局部瓶颈并依据具体的业务场景给出解决方案的领导型人物——Java架构师应…

mysql获取最好成绩对应数据的其他项_开源数据同步神器——canal

前言如今大型的IT系统中&#xff0c;都会使用分布式的方式&#xff0c;同时会有非常多的中间件&#xff0c;如redis、消息队列、大数据存储等&#xff0c;但是实际核心的数据存储依然是存储在数据库&#xff0c;作为使用最广泛的数据库&#xff0c;如何将mysql的数据与中间件的…

window访问不到linux的activemq_springboot整合开源项目测试拦截接口访问的频率

本节目标本篇文章主要是介绍以下springboot整合开源项目&#xff0c;实现反爬虫接口防刷的demo&#xff0c;额外的介绍一下axios的基本用法&#xff1b;所以本篇文章阅读起来相对轻松。OK&#xff0c;下面开始整合.引入项目依赖cn.keking.project kk-anti-reptile …

sql server限制查询条数_18. Django 2.1.7 查询集 QuerySet

上一篇Django 2.1.7 模型 - 条件查询 F对象 Q对象 聚合查询讲述了关于Django模型的介绍F对象、Q对象、聚合查询等功能。不管什么查询&#xff0c;返回的结果都基本是查询集QuerySet&#xff0c;如下&#xff1a;In [16]: MiddlewareInfo.objects.all()Out[16]: 1)>, 2)>,…

ecs 云服务器 管理控制台_阿里云ECS服务器监控资源使用情况

阿里云Ecs服务器运维过程中&#xff0c;无论是Centos系统还是Windows系统&#xff0c;有时候我们需要监控分析最新的服务器资源利用率等运行情况&#xff0c;例如最近3个小时CPU使用率情况、内存使用率、网络流入带宽、网络流出带宽、服务器TCP连接数等数据状况。上述提到的这些…

svg 地图_找地图素材?有这个网站就够了!

文 | 孙宁使用地图类的素材最担心什么&#xff1f;答案是&#xff1a;地图数据的准确性&#xff01;设想&#xff0c;你辛辛苦苦找来的地图素材&#xff0c;却被人发现是一个假的地图&#xff0c;那可是要犯错误滴~今天给大家推荐一个地图网站&#xff0c;完全不用担心这个问题…

hadoop设置java环境_hadoop安装(3)_Linux配置JDK环境

hadoop分布式集群安装的过程中要先给Linux系统配置好JDK环境&#xff0c;因为hadoop是基于Java开发的。所以本文主要介绍如何在centos上安装jdk环境&#xff0c;主要步骤如下&#xff1a;1、去Oracle官网下载Linux版本的jdk&#xff0c;地址如下&#xff1a;笔者下载的是JDK1.8…

sqlplus怎样将名次显示在表的后面_数据透视表-01 3种分析方法比拼,助你步入透视表的知识宝殿...

数据透视表通俗的讲就是一种在EXCEL表中对数据进行分析汇总的方式&#xff0c;它可以方便的排列、汇总复杂数据&#xff0c;更加清晰的查看、分析隐藏在数据内部的信息。另外在菜单栏数据选项下的合并计算和分类汇总也可以进行简单的统计分析下面我们通过一个小例子来了解一下合…

php引用计数器,PHP 引用计数器 通俗版解释_PHP教程

PHP 引用计数器 通俗版解释概述最近看PHP中的引用计数器部分&#xff0c;首先被各种绕晕&#xff0c;然后通过看博客和分析后&#xff0c;总结了一个比较通俗的解释&#xff0c;能帮助自己很好地记忆&#xff0c;也希望能帮助到各位读者。这里分享一遍博文&#xff0c;是比较正…