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,一经查实,立即删除!

相关文章

python os.walk_Python os.walk() 简介

os.walk目录遍历每个月都有那么几天想划水&#xff0c;又到划水的日子了&#xff0c;今天分享的是刚在处理遍历目录相关用到的相关方法。os.walkos.walk的参数如下:os.walk(top, topdownTrue, οnerrοrNone, followlinksFalse)其中&#xff1a;top是要遍历的目录。topdown是代…

python pdf转word 表格_太赞了!Pdf转Word,我用Python 轻松搞定表格和水印!

原标题&#xff1a;太赞了&#xff01;Pdf转Word&#xff0c;我用Python 轻松搞定表格和水印&#xff01;继上一次为大家推出了将pdf转word之后(卧槽&#xff01;Pdf转Word用Python轻松搞定&#xff01;)&#xff0c;引起了大家的热烈讨论&#xff0c;我也总结了大家的一些意见…

dao传递类参数 mybatis_Mybatis (ParameterType) 如何传递多个不同类型的参数

偶然碰到一个需要给xml传一个String类型和一个Integer类型的需求&#xff0c;当时心想用map感觉有点太浪费&#xff0c;所以专门研究了下各种方式。方法一&#xff1a;不需要写parameterType参数public List getXXXBeanList(String xxId, String xxCode);select t.* from table…

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

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

java 异常捕获抛出_JAVA异常处理捕获与抛出原理解析

JAVA 异常当代码运行出现错误导致程序终止运行或出现错误情况的状况&#xff0c;就是异常。异常不是指语法错误&#xff0c;即不属于编译错误&#xff0c;只有运行的程序才会有异常。这个时候&#xff0c;JAVA 就提供了优秀的处理方法&#xff1a;异常处理异常处理能让程序在异…

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

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

python自动输入_使用Python和pywin32自动输入数据

我正在尝试编写一个python脚本来获取数据并将其输入任何形式以下是我目前为止的代码&#xff1a;def pasteNum(n):win32clipboard.OpenClipboard()win32clipboard.EmptyClipboard()win32clipboard.SetClipboardData(win32con.CF_TEXT, str(n))##CTRL-Awin32api.keybd_event(win…

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

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

java 读取 excel poi_Java中读取Excel功能实现_POI

这里使用apache的poi进行读取excel1&#xff0c;新建javaproject 项目&#xff1a;TestExcel2&#xff0c;导入包导入根目录下、lib、ooxml-lib下的所有jar4&#xff0c;操作读取excelimport java.io.File;import java.io.IOException;import java.util.Iterator;import org.ap…

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

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

mysql hbase 同步_HBase 简介和使用 Sqoop 同步 Mysql 数据到 HBase

HBase 数据模型Namespace: 命名空间类似于关系型数据库中的 database schemaTable: 表一个 Namespace 下有多个表&#xff0c;一个表可以包含多个行Row: 行在 HBase 中 Row 由一个 Row Key 和一个或多个列及其值组成&#xff0c;数据值的存储按照 Row Key 的字典顺序存储的。Co…

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

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

java对象怎么创建_java对象是怎么创建出来的

1.使用new关键字这是最常见也是最简单的创建对象的方式了。通过这种方式&#xff0c;我们可以调用任意的构造函数(无参的和带参数的)。Employee emp1 new Employee();0: new #19 // class org/programming/mitra/exercises/Employee3: dup4: invokespecial #21 // Method org/…

看不懂论文代码怎么办_学位论文中的公式排版(制表位+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…

eq值 推荐算法_C++实现十种排序算法

十种排序算法&#xff1a;选择排序插入排序冒泡排序希尔排序快速排序的三种实现方法归并排序堆排序&#xff08;大顶堆&#xff09;计数排序基数排序&#xff08;待实现&#xff09;桶排序&#xff08;待实现&#xff09;#include <bits/stdc.h> using namespace std; vo…

python读取tiff文件进行波段计算_python+tifffile之tiff文件读写方式

背景使用python操作一批同样分辨率的图片&#xff0c;合并为tiff格式的文件。由于opencv主要用于读取单帧的tiff文件&#xff0c;对多帧的文件支持并不好。通过搜索发现了两个比较有用的包&#xff1a;TiffCapture和tifffile。两者都可用pip安装。其中前者主要用于读取tiff文件…

java 线程停止在那个为止_java停止线程

本文将介绍jdk提供的api中停止线程的用法。停止一个线程意味着在一个线程执行完任务之前放弃当前的操作&#xff0c;停止一个线程可以使用Thread.stop()方法&#xff0c;但是做好不要使用它&#xff0c;它是后继jdk版本中废弃的或者将不能使用的方法&#xff0c;大多数停止一个…

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

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