阻塞队列介绍(二)

3 LinkedBlockingQueue

3.1 LinkedBlockingQueue的底层实现

查看LinkedBlockingQueue是如何存储数据,并且实现链表结构的。

// Node对象就是存储数据的单位
static class Node<E> {
// 存储的数据
E item;
// 指向下一个数据的指针
Node<E> next;
// 有参构造
Node(E x) { item = x; }
}

查看LinkedBlockingQueue的有参构造

// 可以手动指定LinkedBlockingQueue的长度,如果没有指定,默认为Integer.MAX_VALUE
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
// 在初始化时,构建一个item为null的节点,作为head和last
// 这种node可以成为哨兵Node,
// 如果没有哨兵节点,那么在获取数据时,需要判断head是否为null,才能找next
// 如果没有哨兵节点,那么在添加数据时,需要判断last是否为null,才能找next
 last = head = new Node<E>(null);
}

 查看LinkedBlockingQueue的其他属性

// 因为是链表,没有想数组的length属性,基于AtomicInteger来记录长度
private final AtomicInteger count = new AtomicInteger();
// 链表的头,取
transient Node<E> head;
// 链表的尾,存
private transient Node<E> last;
// 消费者的锁
private final ReentrantLock takeLock = new ReentrantLock();
// 消费者的挂起操作,以及唤醒用的condition
private final Condition notEmpty = takeLock.newCondition();
// 生产者的锁
private final ReentrantLock putLock = new ReentrantLock();
// 生产者的挂起操作,以及唤醒用的condition
private final Condition notFull = putLock.newCondition();

3.2 生产者方法实现原理

3.2.1 add方法

你懂得,还是走offer方法

public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}

3.2.2 offer方法

public boolean offer(E e) {
// 非空校验
if (e == null) throw new NullPointerException();
// 拿到存储数据条数的count
final AtomicInteger count = this.count;
// 查看当前数据条数,是否等于队列限制长度,达到了这个长度,直接返回false
if (count.get() == capacity)
return false;
// 声明c,作为标记存在
int c = -1;
// 将存储的数据封装为Node对象
Node<E> node = new Node<E>(e);
// 获取生产者的锁。
final ReentrantLock putLock = this.putLock;
// 竞争锁资源
putLock.lock();
try {// 再次做一个判断,查看是否还有空间
if (count.get() < capacity) {
// enqueue,扔数据
enqueue(node);
// 将数据个数 + 1
c = count.getAndIncrement();
// 拿到count的值 小于 长度限制
// 有生产者在基于await挂起,这里添加完数据后,发现还有空间可以存储数据,
// 唤醒前面可能已经挂起的生产者
// 因为这里生产者和消费者不是互斥的,写操作进行的同时,可能也有消费者在消费数据。
if (c + 1 < capacity)
// 唤醒生产者
notFull.signal();
}
} finally {
// 释放锁资源
putLock.unlock();
}
// 如果c == 0,代表添加数据之前,队列元素个数是0个。
// 如果有消费者在队列没有数据的时候,来消费,此时消费者一定会挂起线程
if (c == 0)
// 唤醒消费者
signalNotEmpty();
// 添加成功返回true,失败返回-1
return c >= 0;
}
//================================================
private void enqueue(Node<E> node) {
// 将当前Node设置为last的next,并且再将当前Node作为last
last = last.next = node;
}
//================================================
private void signalNotEmpty() {
// 获取读锁
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 唤醒。
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
sync -> wait / notify

3.2.3 offer(time,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 {
// 如果元素个数和限制个数一致,直接准备挂起
while (count.get() == capacity) {
// 挂起的时间是不是已经没了
if (nanos <= 0)
// 添加失败,返回false
return false;
// 挂起线程
nanos = notFull.awaitNanos(nanos);
}
// 有空余位置,enqueue添加数据
enqueue(new Node<E>(e));
// 元素个数 + 1
c = count.getAndIncrement();
// 当前添加完数据,还有位置可以添加数据,唤醒可能阻塞的生产者
if (c + 1 < capacity)
notFull.signal();
} finally {
// 释放锁
putLock.unlock();}
// 如果之前元素个数是0,唤醒可能等待的消费者
if (c == 0)
signalNotEmpty();
return true;
}

3.2.4 put方法

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();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}

3.3 消费者方法实现原理

从remove方法开始,查看消费者获取数据的方式

3.3.1 remove方法

public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}

3.3.2 poll方法

public E poll() {
// 拿到队列数据个数的计数器
final AtomicInteger count = this.count;// 当前队列中数据是否0
if (count.get() == 0)
// 说明队列没数据,直接返回null即可
return null;
// 声明返回结果
E x = null;
// 标记
int c = -1;
// 获取消费者的takeLock
final ReentrantLock takeLock = this.takeLock;
// 加锁
takeLock.lock();
try {
// 基于DCL,确保当前队列中依然有元素
if (count.get() > 0) {
// 从队列中移除数据
x = dequeue();
// 将之前的元素个数获取,并--
c = count.getAndDecrement();
if (c > 1)
// 如果依然有数据,继续唤醒await的消费者。
notEmpty.signal();
}
} finally {
// 释放锁资源
takeLock.unlock();
}// 如果之前的元素个数为当前队列的限制长度,
// 现在消费者消费了一个数据,多了一个空位可以添加
if (c == capacity)
// 唤醒阻塞的生产者
signalNotFull();
return x;
}
//================================================
private E dequeue() {
// 拿到队列的head位置数据
Node<E> h = head;
// 拿到了head的next,因为这个是哨兵Node,需要拿到的head.next的数据
Node<E> first = h.next;
// 将之前的哨兵Node.next置位null。help GC。
h.next = h;
// 将first置位新的head
head = first;
// 拿到返回结果first节点的item数据,也就是之前head.next.item
E x = first.item;
// 将first数据置位null,作为新的head
first.item = null;
// 返回数据
return x;
}
//================================================
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
// 唤醒生产者。
notFull.signal();
} finally {
putLock.unlock();
}
}

3.3.3 poll(time,unit)方法

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
// 返回结果
E x = null;
// 标识
int c = -1;
// 将挂起实现设置为纳秒级别
long nanos = unit.toNanos(timeout);
// 拿到计数器
final AtomicInteger count = this.count;
// take锁加锁
final ReentrantLock takeLock = this.takeLock;takeLock.lockInterruptibly();
try {
// 如果没数据,进到while
while (count.get() == 0) {
if (nanos <= 0)
return null;
// 挂起当前线程
nanos = notEmpty.awaitNanos(nanos);
}
// 剩下内容,和之前一样。
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}

3.3.4 take方法

public E take() throws InterruptedException {
E x;int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 相比poll(time,unit)方法,这里的出口只有一个,就是中断标记位,抛出异常,否则一直等待
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}

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

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

相关文章

C++多线程学习(三):锁资源管理和条件变量

参考引用 C11 14 17 20 多线程从原理到线程池实战代码运行环境&#xff1a;Visual Studio 2019 1. 利用栈特性自动释放锁 RAII 1.1 什么是 RAII RAII (Resource Acquisition Is Initialization)&#xff1a;使用局部对象来管理资源的技术称为资源获取即初始化 它的生命周期是…

快速了解软件工程学概述(5种软件过程模型)

目录 1 、什么是软件&#xff1f;特点有哪些 &#xff1f; 2 、 软件危机 定义&#xff1a; 软件危机产生的原因 消除软件危机的方法 3 、软件工程 1.软件工程的介绍 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;本质特征 (3)软件工程方法学&#xff08;方…

十八数字文化受邀参加版博会“区块链+版权”创新应用试点研讨会

2023年11月23日至25日&#xff0c;以“版权新时代 赋能新发展”为主题的第九届中国国际版权博览会在成都市中国西部国际博览城和天府国际会议中心举办。版博会是我国版权领域唯一的综合性、国际性、国家级版权专业博览会&#xff0c;本届版博会由国家版权局主办&#xff0c;四川…

宏工科技通过CMMI三级认证,软件研发能力获国际权威认可

近日&#xff0c;宏工科技子公司湖南宏工软件成功通过CMMI三级认证并正式获得资质证书&#xff0c;斩获全球软件领域最权威的认证之一&#xff0c;标志着宏工科技在软件技术开发、研发管理、项目管理等多方面获得国际权威认证。 CMMI全称是Capability Maturity Model Integrati…

ESXi 6.7 升级 7.0

方式一&#xff1a;esxcli方式 1.登陆exsi web界面。 启用控制台shell 2.存储-datastore-数据存储浏览器&#xff0c;上载 ESXI-7.0.0-depot.zip升级文件。记住此datastore的位置 ssh连接ESXI主机 vmware -vl 查看当前版本 查看升级包中对应的版本信息&#xff1a; es…

3D模型材质编辑器

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 材质贴图&#xff08;Texture Mapping&#xff09;&#xff1a;是在物体着色方面最引人注目、…

使用 yum 出现 Loaded plugins: fastestmirro

&#x1f4d1;前言 本文主要是使用 yum 出现 Loaded plugins: fastestmirro&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日…

注意力机制(Q,K,V)基本概念

文章目录 一、注意力提示1.1概念1.2生活中的注意力提示1.3注意力机制基本框架小结 二、注意力汇聚2.1概念2.2非参注意力汇聚2.2.1平均汇聚2.2.2Nadaraya-Waston核回归 2.3通用注意力汇聚公式2.4带参数注意力汇聚小结 三、注意力评分函数3.1概念3.2例子 四、遮蔽softmax三四小结…

10. Mysql 分组或汇总查询

Mysql 函数参考和扩展&#xff1a;Mysql 常用函数和基础查询、 Mysql 官网 Mysql 语法执行顺序如下&#xff0c;一定要清楚&#xff01;&#xff01;&#xff01;运算符相关&#xff0c;可前往 Mysql 基础语法和执行顺序扩展。 (8) select (9) distinct (11)<columns_name…

【触想智能】无风扇工控电脑一体机使用优势分析

无风扇工控电脑一体机是属于工控一体机分类中的其中一种&#xff0c;看名字&#xff0c;很明显就是没有散热风扇的工控电脑一体机&#xff0c;而平常我们使用的电脑主机是带有电源风扇、CPU散热风扇的。 无风扇工控电脑一体机的配置组成和商用电脑主机的配置基本一样&#xff0…

大便后的多巴胺

大便后的多巴胺 -- 轻松愉快首先&#xff0c;人不管“拉”什么屎&#xff0c;都会很爽。人类的许多快感&#xff0c;都来源于从身体里捣鼓出去一些东西的时候。便便走了&#xff0c;肠道压力变小&#xff0c;如释重负的神经系统就会分泌“多巴胺”&#xff0c;让我们感到格外的…

C#常见的设计模式-结构型模式

引言 设计模式是软件工程中用于解决常见问题的可复用解决方案。在C#编程中&#xff0c;常见的设计模式具有广泛的应用。本篇博客将重点介绍C#中常见的结构型设计模式&#xff0c;包括适配器模式、装饰器模式、代理模式、组合模式和享元模式。 目录 引言1. 适配器模式(Adapter …

Selenium/webdriver原理解析

最近在看一些底层的东西。driver翻译过来是驱动&#xff0c;司机的意思。如果将webdriver比做成司机&#xff0c;竟然非常恰当。 我们可以把WebDriver驱动浏览器类比成出租车司机开出租车。在开出租车时有三个角色&#xff1a; 乘客&#xff1a;他/她告诉出租车司机去哪里&…

Python语言创建爬虫代理IP池详细步骤和代码示例

目录 一、引言 二、代理IP的选择 三、使用代理IP的代码示例 四、创建代理IP池的代码示例 五、总结 一、引言 在爬虫程序中&#xff0c;代理IP的使用是避免IP被封禁、提高爬取效率的重要手段。本文将详细介绍如何使用Python语言创建一个爬虫代理IP池&#xff0c;包括代理I…

Maven 命令之将本地 Jar 包安装到 Maven 本地仓库

1、前言 Maven 是 Java 平台下的一款项目构建和依赖管理的自动化管理工具。 通过 Maven 远程仓库地址我们可以方便的管理 Jar 依赖包&#xff0c;但是在实际项目中有时候存在远程仓库中没有的 Jar 包&#xff0c;我们在项目中又必须要使用它&#xff0c;那就需要把本地 Jar 添…

中科驭数受邀出席2023 ODCC冬季全会,共谋开放数据中心创新发展

近日&#xff0c;2023年开放数据中心委员会&#xff08;简称“ODCC”&#xff09;冬季全会在宁夏银川成功召开&#xff0c;中科驭数作为ODCC的新成员单位&#xff0c;受邀出席本次重要会议。 ▲ 中科驭数正式加入ODCC开放数据中心委员会 开放数据中心委员会是在中国通信标准化…

排序篇(六)----排序小结(不用三连,混流量券)

排序篇(六)----排序小结 排序算法复杂度及稳定性分析 直接插入排序的算法复杂度&#xff1a; 最好情况下&#xff0c;当数组已经有序时&#xff0c;直接插入排序的时间复杂度为O(n)&#xff0c;其中n是数组的大小。最坏情况下&#xff0c;当数组逆序排列时&#xff0c;直接插…

Lazada测评怎么做?

国内电商行业的发展日趋激烈&#xff0c;卖家想要脱颖而出非常困难&#xff0c;许多卖家选择入驻跨境电商平台开店&#xff0c; 跨境电商平台吸引了许多卖家入驻&#xff0c;而最近有很多朋友在私信问我关于Lazada测评的一些事情 Lazada产品测评流程步骤 怎么测评 这个怎么测…

pc数据通过插槽来设置启用未启用

使用三元表达式 <el-table-column prop"state" label"启用" width"180"><template v-slot"{ row }"><span>{{row.state 1 ? "已启用" : row.state 0 ? "未启用" : "无"}}</sp…

基于AC6969的蓝牙控制RGB彩灯

程序的实现思路&#xff1a;单片机与手机app之间通过蓝牙实现通讯&#xff0c;通过点击屏幕上的对应色块然后app会把对应的RGB值发送到单片机。然后单片机会对数据进行解析然后把数字量转换为模拟量&#xff0c;然后通过PWM控制IO口输出不同的电压以此来达到控制RGB灯 RGB彩灯原…