Java中的阻塞队列-LinkedBlockingQueue(二)

原文地址:http://benjaminwhx.com/2018/05/11/%E3%80%90%E7%BB%86%E8%B0%88Java%E5%B9%B6%E5%8F%91%E3%80%91%E8%B0%88%E8%B0%88LinkedBlockingQueue/

在集合框架里,想必大家都用过ArrayList和LinkedList,也经常在面试中问到他们之间的区别。ArrayList和ArrayBlockingQueue一样,内部基于数组来存放元素,而LinkedBlockingQueue则和LinkedList一样,内部基于链表来存放元素。

LinkedBlockingQueue实现了BlockingQueue接口,这里放一张类的继承关系图(图片来自之前的文章:说说队列Queue)

LinkedBlockingQueue不同于ArrayBlockingQueue,它如果不指定容量,默认为Integer.MAX_VALUE,也就是无界队列。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小。

2、源码分析

2.1 属性

/*** 节点类,用于存储数据*/
static class Node<E> {E item;Node<E> next;Node(E x) { item = x; }
}/** 阻塞队列的大小,默认为Integer.MAX_VALUE */
private final int capacity;/** 当前阻塞队列中的元素个数 */
private final AtomicInteger count = new AtomicInteger();/*** 阻塞队列的头结点*/
transient Node<E> head;/*** 阻塞队列的尾节点*/
private transient Node<E> last;/** 获取并移除元素时使用的锁,如take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();/** notEmpty条件对象,当队列没有数据时用于挂起执行删除的线程 */
private final Condition notEmpty = takeLock.newCondition();/** 添加元素时使用的锁如 put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();/** notFull条件对象,当队列数据已满时用于挂起执行添加的线程 */
private final Condition notFull = putLock.newCondition();

从上面的属性我们知道,每个添加到LinkedBlockingQueue队列中的数据都将被封装成Node节点,添加的链表队列中,其中head和last分别指向队列的头结点和尾结点。与ArrayBlockingQueue不同的是,LinkedBlockingQueue内部分别使用了takeLock 和 putLock 对并发进行控制,也就是说,添加和删除操作并不是互斥操作,可以同时进行,这样也就可以大大提高吞吐量。

这里如果不指定队列的容量大小,也就是使用默认的Integer.MAX_VALUE,如果存在添加速度大于删除速度时候,有可能会内存溢出,这点在使用前希望慎重考虑。

另外,LinkedBlockingQueue对每一个lock锁都提供了一个Condition用来挂起和唤醒其他线程。

构造函数

public LinkedBlockingQueue() {// 默认大小为Integer.MAX_VALUEthis(Integer.MAX_VALUE);
}public LinkedBlockingQueue(int capacity) {if (capacity <= 0) throw new IllegalArgumentException();this.capacity = capacity;last = head = new Node<E>(null);
}public LinkedBlockingQueue(Collection<? extends E> c) {this(Integer.MAX_VALUE);final ReentrantLock putLock = this.putLock;putLock.lock();try {int n = 0;for (E e : c) {if (e == null)throw new NullPointerException();if (n == capacity)throw new IllegalStateException("Queue full");enqueue(new Node<E>(e));++n;}count.set(n);} finally {putLock.unlock();}
}

默认的构造函数和最后一个构造函数创建的队列大小都为Integer.MAX_VALUE,只有第二个构造函数用户可以指定队列的大小。第二个构造函数最后初始化了last和head节点,让它们都指向了一个元素为null的节点。

方法

同样,LinkedBlockingQueue也有着和ArrayBlockingQueue一样的方法,我们先来看看入队列的方法。

2.3.1、入队方法

LinkedBlockingQueue提供了多种入队操作的实现来满足不同情况下的需求,入队操作有如下几种:

  • void put(E e);
  • boolean offer(E e);
  • boolean offer(E e, long timeout, TimeUnit unit)。

put(E e)

public void put(E e) throws InterruptedException {if (e == null) throw new NullPointerException();int c = -1;Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;// 获取锁中断
    putLock.lockInterruptibly();try {//判断队列是否已满,如果已满阻塞等待while (count.get() == capacity) {notFull.await();}// 把node放入队列中
        enqueue(node);c = count.getAndIncrement();// 再次判断队列是否有可用空间,如果有唤醒下一个线程进行添加操作if (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}// 如果队列中有一条数据,唤醒消费线程进行消费if (c == 0)signalNotEmpty();
}

小结put方法来看,它总共做了以下情况的考虑:

  • 队列已满,阻塞等待。
  • 队列未满,创建一个node节点放入队列中,如果放完以后队列还有剩余空间,继续唤醒下一个添加线程进行添加。如果放之前队列中没有元素,放完以后要唤醒消费线程进行消费。

offer(E e)

public boolean offer(E e) {if (e == null) throw new NullPointerException();final AtomicInteger count = this.count;if (count.get() == capacity)return false;int c = -1;Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;putLock.lock();try {// 队列有可用空间,放入node节点,判断放入元素后是否还有可用空间,// 如果有,唤醒下一个添加线程进行添加操作。if (count.get() < capacity) {enqueue(node);c = count.getAndIncrement();if (c + 1 < capacity)notFull.signal();}} finally {putLock.unlock();}if (c == 0)signalNotEmpty();return c >= 0;
}

可以看到offer仅仅对put方法改动了一点点,当队列没有可用元素的时候,不同于put方法的阻塞等待,offer方法直接方法false。

offer(E e, long timeout, TimeUnit unit)

public boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException {if (e == null) throw new NullPointerException();long nanos = unit.toNanos(timeout);int c = -1;final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;putLock.lockInterruptibly();try {// 等待超时时间nanos,超时时间到了返回falsewhile (count.get() == capacity) {if (nanos <= 0)return false;nanos = notFull.awaitNanos(nanos);}enqueue(new Node<E>(e));c = count.getAndIncrement();if (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}if (c == 0)signalNotEmpty();return true;
}

该方法只是对offer方法进行了阻塞超时处理,使用了Condition的awaitNanos来进行超时等待,这里为什么要用while循环?因为awaitNanos方法是可中断的,为了防止在等待过程中线程被中断,这里使用while循环进行等待过程中中断的处理,继续等待剩下需等待的时间。

转载于:https://www.cnblogs.com/liyongliang/p/10697876.html

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

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

相关文章

碳钢腐蚀速率计算公式_镁合金轮毂螺栓连接的电偶腐蚀行为

环境污染和能源短缺促使日益发达的汽车工业大力推进构件轻量化&#xff0c;镁合金是最轻的结构材料之一&#xff0c;构件采用镁合金制造可以在减重的同时不降低结构强度&#xff0c;受到汽车工业的青睐。轮毂作为汽车的主要组成部件&#xff0c;其轻量化是汽车节能减排的有效途…

避免人为灾难:盘点数据中心里十大愚蠢行为

对于企业运营&#xff0c;数据中心从设计、部署等各个环节都有极其严格的规范&#xff0c;保证简单的“题目”不出错也需要企业IT管理人员的智慧&#xff0c;在数据中心任何一个小错误往往会带来巨大灾难。数据中心从设计、部署、测试、运行、运维等各个环节都不能有任何的疏忽…

leetcode1039. 多边形三角剖分的最低得分(动态规划)

给定 N&#xff0c;想象一个凸 N 边多边形&#xff0c;其顶点按顺时针顺序依次标记为 A[0], A[i], …, A[N-1]。 假设您将多边形剖分为 N-2 个三角形。对于每个三角形&#xff0c;该三角形的值是顶点标记的乘积&#xff0c;三角剖分的分数是进行三角剖分后所有 N-2 个三角形的…

thinkcmf 横向排列数据_利用python进行数据分析之数据清洗规整

1.处理缺失值数据使用dropna()时&#xff0c;注意里面参数axis、how、thresh的用法使用fillna()时&#xff0c;注意里面参数value、method、inplace、limit的用法2.数据转换去重data.drop_duplicates(keeplast)#注意keep的用法映射map&#xff08;&#xff09;针对的是一维数组…

v$asm_diskgroup中state的说明

1.使用oracle账号连接数据库&#xff0c;查看v$asm_diskgroup 2.使用grid账号连接ASM实例&#xff0c;查看v$asm_diskgroup 3.官方v$asm_diskgroup关于state的解释 https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-ASM_DISKGROUP.html#GUID-5CF77719-7…

Python笔记 【无序】 【五】

描述符 将某种特殊类型的类【只要实现了以下或其中一个】的实例指派给另一个类的属性 1.__get__(self,instance,owner)//访问属性&#xff0c;返回属性的值 2.__set__(self,instance,value)//将在属性分配【即赋值】中调用&#xff0c;不返回任何内容 3.__delete__&#xff08;…

化工图纸中LISP_化工设备厂参展模型设计制作

最近这个案子是受某化工设备企业委托做四套设备模型 用来参加展会在模型制作过程中&#xff0c;这类案例经常遇到。但是客户所提供的CAD图纸&#xff0c;往往是实物尺寸在进行缩放的过程中常会造成过薄和过于精细的情况出现眼下技术小哥就遇到这类情况让我们先看看客户提供的C…

社交大佬们的数据“大”在哪里?

文章讲的是社交大佬们的数据“大”在哪里&#xff0c;“别说忙&#xff0c;没工夫看书&#xff0c;你那刷FB/朋友圈的工夫腾出来&#xff0c;保证每周啃下一本”&#xff0c;小编身边总充斥着这样的“训话”。 额&#xff0c;奈何我每天的工作离不开从社交媒体中获取信息&#…

mysql 多数据源访问_通过Spring Boot配置动态数据源访问多个数据库的实现代码

之前写过一篇博客《SpringMybatisMysql搭建分布式数据库访问框架》描述如何通过SpringMybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了)&#xff1a;只适用于数据库数量不多且固定的情况。针对数据库动态增加的情况无能为力。下面讲的方案能支…

菜鸟postman接口测试_postman 接口测试(转)

本文转载自testerhome&#xff1b;作者&#xff1a;xinxi1990 &#xff1b;原文链接&#xff1a;https://testerhome.com/topics/18719&#xff1b;转载以分享知识为目的&#xff0c;著作权归原作者所有&#xff0c;如有侵权&#xff0c;请联系删除。postman使用创建用例集启动…

vb 数组属性_VB中菜单编辑器的使用讲解及实际应用

大家好&#xff0c;今天我们共同来学习VB中菜单方面的知识。VB中菜单的基本作用有两个&#xff1a;1、提供人机对话的界面&#xff0c;以便让使用者选择应用系统的各种功能&#xff1b;2、管理应用系统&#xff0c;控制各种功能模块的运行。在实际应用中&#xff0c;菜单可分为…

《JAVA程序设计》_第七周学习总结

一、学习内容 1.String类——8,1知识 Java专门提供了用来处理字符序列的String类。String类在java.lang包中&#xff0c;由于java.lang包中的类被默认引入&#xff0c;因此程序可以直接使用String类。需要注意的是Java把String类声明为final类&#xff0c;因此用户不能扩展Stri…

NeHe OpenGL教程 第三十七课:卡通映射

转自【翻译】NeHe OpenGL 教程 前言 声明&#xff0c;此 NeHe OpenGL教程系列文章由51博客yarin翻译&#xff08;2010-08-19&#xff09;&#xff0c;本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写&#xff0c;以及yarn的翻译整理表示感谢。 NeHe OpenGL第三十七…

SDN交换机在云计算网络中的应用场景

SDN的技术已经发展了好几年了&#xff0c;而云计算的历史更长&#xff0c;两者的结合更是作为SDN的一个杀手级应用在近两年炒得火热&#xff0c;一些知名咨询公司的关于SDN逐年增加的市场份额的论断&#xff0c;也主要是指SDN在云计算网络中的应用。 关于SDN在云计算网络中的应…

sql server 里面怎么支持数字使用双引号_国查:用中文编写SQL

这两天被 文言(wenyan-lang)刷屏了&#xff0c;这个项目在于使用文言文进行编程&#xff0c;我打算蹭个热度&#xff0c;把年初的作品再捞一捞&#xff0c;即中文SQL。1. 文言Wenyan&#xff1a;吾有一數。曰三。名之曰「甲」。為是「甲」遍。吾有一言。曰「「問天地好在。」」…

革新以太网交换机架构 全光网络的风刮进园区

全光网络的风正在刮进园区网&#xff0c;众所周知&#xff0c;光纤入户发展迅速&#xff0c;随着PON&#xff08;无源光纤网络&#xff09;技术在运营商通信网络的大规模使用&#xff0c;PON相关产业链逐步成熟&#xff0c;这也使得PON技术逐步在企业园区网得到应用。 基于铜线…

漫谈单点登录(SSO)(淘宝天猫)(转载)

1. 摘要 &#xff08; 注意&#xff1a;请仔细看下摘要&#xff0c;留心此文是否是您的菜&#xff0c;若浪费宝贵时间&#xff0c;深感歉意&#xff01;&#xff01;&#xff01;&#xff09; SSO这一概念由来已久&#xff0c;网络上对应不同场景的成熟SSO解决方案比比皆是&…

越狱第一至五季/全集迅雷下载

越狱 第一季 Prison Break Season 1 (2005) 本季看点&#xff1a;迈克尔斯科菲尔德是一头陷于绝境欲拼死一搏的怒狮——他的哥哥林肯巴罗斯被认定犯有谋杀罪被投入了福克斯河监狱的死囚牢。虽然所有的证据都指出林肯就是凶手&#xff0c;迈克尔坚信兄长是无辜的。林肯的死刑执行…

java -jar 默认参数_JAVA入门学习指南,建议收藏

如果你不懂Java 并且想认真学习接触了解一下Java的语法&#xff0c;建议把这篇文章收藏了&#xff0c;多看几遍&#xff0c;应该可以初步掌握Java 大部分基础的语法 。 让我们出发吧&#xff01;ps:本文有点长&#xff0c;耐心阅读 。〇&#xff0c;编程环境工程项目推荐使用ID…

【RabbitMQ】 WorkQueues

消息分发 在【RabbitMQ】 HelloWorld中我们写了发送/接收消息的程序。这次我们将创建一个Work Queue用来在多个消费者之间分配耗时任务。 Work Queues&#xff08;又称为&#xff1a;Task Queues&#xff09;的主要思想是&#xff1a;尽可能的减少执行资源密集型任务时的等待时…