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

相关文章

自动加密企业关键业务数据 赛门铁克推出全新信息保护解决方案

最新推出的Symantec Information Centric Security解决方案&#xff0c;能够帮助企业随时随地对数据进行自动加密、跟踪和撤销&#xff0c;提供卓越的可见性和管控力 近日&#xff0c;全球网络安全领域的领导者赛门铁克公司宣布推出一款全新的高级信息保护工具 Symantec Inform…

leetcode312. 戳气球(动态规划)

有 n 个气球&#xff0c;编号为0 到 n-1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。如果你戳破气球 i &#xff0c;就可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻…

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

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

第七周总结

2019第七周作业 本周作业头 这个作业属于那个课程C语言程序设计II这个作业要求在哪里https://edu.cnblogs.com/campus/zswxy/computer-scienceclass1-2018/homework/2939我在这个课程的目标是理解指针数组和地址之前的关系及应用这个作业在那个具体方面帮助我实现目标practice参…

python大纲图_Python课程大纲

课程大纲被分成6个部分&#xff0c;每个部分又被分解为多个阶段&#xff0c; 而每个阶段包含了多个Try, Workshop, FactToFace, Apply. 这里只列出部分&#xff0c;和阶段&#xff1a;CHAPTER 0 : 预科[可选]Linux使用&#xff0c;常用CMD&#xff0c;服务配置&#xff0c;IDE&…

如何使用Google Authenticator在ASP.NET Core中设置两因素身份验证

介绍 (Introduction) In this article, we are going to learn how to perform two-factor authentication in an ASP.NET Core application using the Google Authenticator app.在本文中&#xff0c;我们将学习如何使用Google Authenticator应用程序在ASP.NET Core应用程序中…

280. Wiggle Sort

最后更新 二刷 这个题做得真蠢。上来想的复杂了&#xff0c;想的是quick sort之类的&#xff0c;然后一个一个交换。 实际上直接交换就行。。没啥特别的。 回头看一刷也是同样的思考过程 宿命论啊。。 Time: O(n) Space: O(1) public class Solution {public void wiggleSort(i…

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

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

python中node.tag的用法_python在ui自动化中的一些常见用法

http://cn.python-requests.org/zh_CN/latest 可以查看requests库的说明&#xff0c;pprint(res.json(),width30)可以对请求的返回值按照json格式化形式进行打印。常见的content-type 有application/x-www-form-urlencoded、application/json、application/xml。自动化测试操作…

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

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

TRIZ解决问题方法

个人觉的成功是有规律的&#xff0c;那些成功的人士&#xff0c;都有一套处理事情的秘籍。只要我们的思维方式把那些秘籍融会贯通&#xff0c;并快速执行&#xff0c;我们有一天也会成功的。 TRIZ解决问题的5点方法。 1.确定最终目标。 2.列出阻碍因素 3.消除阻碍因素 4.可以利…

windows调用python_windows 快捷调用Python语言

本文主要向大家介绍了windows 快捷调用Python语言&#xff0c;通过具体的内容向大家展示&#xff0c;希望对大家学习Python语言有所帮助。场景1&#xff1a;某云平台的账号/或密码比较长&#xff0c;一旦浏览器缓存失效&#xff0c;就要去邮件/文件查找&#xff0c;费时费力场景…

《量化投资:以MATLAB为工具》连载(1)基础篇-N分钟学会MATLAB(上)

http://blog.sina.com.cn/s/blog_4cf8aad30102uylf.html 《量化投资&#xff1a;以MATLAB为工具》连载(1)基础篇-N分钟学会MATLAB&#xff08;上&#xff09; 《量化投资&#xff1a;以MATLAB为工具》简介 《量化投资&#xff1a;以MATLAB为工具》是由电子工业出版社&#xff0…

android-开源项目_我如何摆脱对开源的恐惧,并开始了自己的项目-以及如何做到。...

android-开源项目by Linea Brink Andersen通过Linea Brink Andersen 我如何摆脱对开源的恐惧&#xff0c;并开始了自己的项目-以及如何做到。 (How I crushed my fear of open source and started my own project — and how you can, too.) A week ago, I started an Open So…

本题要求实现函数输出n行数字金字塔。_练习5-3 数字金字塔 (15分)

本题要求实现函数输出n行数字金字塔。函数接口定义&#xff1a;void pyramid( int n );其中n是用户传入的参数&#xff0c;为[1, 9]的正整数。要求函数按照如样例所示的格式打印出n行数字金字塔。注意每个数字后面跟一个空格。裁判测试程序样例&#xff1a;#include <stdio.…

leetcode167. 两数之和 II - 输入有序数组(二分查找)

给定一个已按照升序排列 的有序数组&#xff0c;找到两个数使得它们相加之和等于目标数。 函数应该返回这两个下标值 index1 和 index2&#xff0c;其中 index1 必须小于 index2。 说明: 返回的下标值&#xff08;index1 和 index2&#xff09;不是从零开始的。 你可以假设每…

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…

AutoMapper的介绍与使用(二)

AutoMapper的匹配 1&#xff0c;智能匹配 AutoMapper能够自动识别和匹配大部分对象属性: 如果源类和目标类的属性名称相同&#xff0c;直接匹配&#xff0c;不区分大小写目标类型的CustomerName可以匹配源类型的Customer.Name目标类型的Total可以匹配源类型的GetTotal()方法…

站长快讯 WordPress跨站攻击漏洞修补

WordPress中发现一些漏洞&#xff0c;攻击者利用该漏洞可以发起跨站脚本攻击&#xff0c;绕过WordPress安全性限制&#xff0c;获取较为敏感的修订历史记录的信息&#xff0c;或者绑架站点以用于DDoS攻击。 CVE ID CVE-2015-8834 CVE-2016-5832 CVE-2016-5834 CVE-2016-5835 C…