【源码解析】聊聊阻塞队列之BlockingArrayQueue

阻塞队列

阻塞队列:顾名思义 首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下入所示。

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞。
  • 当阻塞队列时满的时,往队列里添加元素的操作将会被阻塞。

试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。

同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程从队列中移除一个元素才可以插入队列中。

为什么使用阻塞队列 好处?

在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒。

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BolckingQueue都给你一手包办了,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

阻塞队列接口和实现类

实现类特点
ArrayBlockingQueue由数组结构组成的有界阻塞队列
LinkedBlockingQueue由链表结构组成的有界(但大小默认值有Integer.MAX_VALUE)阻塞队列
PriorityBlockingQueue支持优先级排序的无界阻塞队列
DelayQueue使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue不存储元素的阻塞队列,也既单个元素的队列
LinkedTransferQueue由链表结构组成的无界阻塞队列
LinkedBlockingDeque由链表结构组成的双向阻塞队列

BlockingQueue

//阻塞队列
public interface BlockingQueue<E> extends Queue<E> {//   将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量
//  在成功时返回 true,如果此队列已满,则抛IllegalStateException。boolean add(E e);//插入队列的尾部boolean offer(E e);void put(E e) throws InterruptedException;//插入队列的尾部,可以设置等待时间,不成功抛出异常boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException;//移除对头部元素。如果没有元素会阻塞E take() throws InterruptedException;//移除对头元素E poll(long timeout, TimeUnit unit)throws InterruptedException;
}

插入方法:

add(E e) : 添加成功返回true,失败抛IllegalStateException异常
  offer(E e) : 成功返回 true,如果此队列已满,则返回 false。
  put(E e) :将元素插入此队列的尾部,如果该队列已满,则一直阻塞 // 推荐使用这个
删除方法:

remove(Object o) :移除指定元素,成功返回true,失败返回false
  poll() : 获取并移除此队列的头元素,若队列为空,则返回 null
  take():获取并移除此队列头元素,若没有元素则一直阻塞。 //推荐使用这个

ArrayBlockingQueue

内部是通过冲入锁reentrantLock和Condition条件队列实现的,即有公平访问和非公平访问两种方式。

一个测试demo

public class BlockingArrayQueue {public static void main(String[] args) {BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(1);new Thread(new Product(blockingQueue)).start();new Thread(new Consumer(blockingQueue)).start();}}class Product implements Runnable{private BlockingQueue<String> blockingQueue;public Product(BlockingQueue blockingQueue) {this.blockingQueue = blockingQueue;}@Overridepublic void run() {while (true) {try {System.out.println("添加元素了");blockingQueue.put(String.valueOf(UUID.randomUUID()));} catch (InterruptedException e) {e.printStackTrace();}}}
}class Consumer implements Runnable{private BlockingQueue<String> blockingQueue;public Consumer(BlockingQueue blockingQueue) {this.blockingQueue = blockingQueue;}@Overridepublic void run() {while (true) {try {String take = blockingQueue.take();System.out.println("消费元素了  "+take);TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}
}

构造方法

    //默认是非公平锁的方式public ArrayBlockingQueue(int capacity) {this(capacity, false);}//可以通过参数进行设置是否使用公平锁public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];//使用的是 ReentrantLock 去判断是否使用公平锁lock = new ReentrantLock(fair);//使用的condition 非空和非满的条件notEmpty = lock.newCondition();notFull =  lock.newCondition();}

属性

    /** The queued items */// 存储数据的数组 使用的是一个object数组final Object[] items;/** items index for next take, poll, peek or remove *///获取数据的索引int takeIndex;/** items index for next put, offer, or add *///添加数据的索引int putIndex;/** Number of elements in the queue *///队列元素的个数int count;/** Main lock guarding all access *///控制并发访问的锁ReentrantLockfinal ReentrantLock lock;// notEmpty条件对象,用于通知take方法队列已有元素,可执行获取操作/** Condition for waiting takes */private final Condition notEmpty;// notFull条件对象,用于通知put方法队列未满,可执行添加操作/** Condition for waiting puts */private final Condition notFull;//迭代器transient Itrs itrs = null;

添加

   //add方法 其实调用的是offer()//加入数据成功 返回true//加入失败就抛出异常public boolean add(E e) {return super.add(e);}public boolean offer(E e) {//判断是否为空checkNotNull(e);final ReentrantLock lock = this.lock;//加锁操作lock.lock();try {//如果当前队列满 返回falseif (count == items.length)return false;else {// 添加元素到队列中enqueue(e);return true;}} finally {//释放锁lock.unlock();}}//阻塞方法public void put(E e) throws InterruptedException {checkNotNull(e);final ReentrantLock lock = this.lock;lock.lockInterruptibly(); //该方法可被中断try {//当队列元素和数组长度相等  当前线程挂起,添加到notFull条件队列中等待唤醒while (count == items.length)notFull.await();//不满则,直接添加元素enqueue(e);} finally {lock.unlock();}}

获取

   public E poll() {final ReentrantLock lock = this.lock;lock.lock();try {//队列元素为空 返回nullreturn (count == 0) ? null : dequeue();} finally {lock.unlock();}}public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)//如果没有元素 会进行阻塞notEmpty.await();return dequeue();} finally {lock.unlock();}}//直接返回队列头的元素 不进行修改public E peek() {final ReentrantLock lock = this.lock;lock.lock();try {return itemAt(takeIndex); // null when queue is empty} finally {lock.unlock();}}

在这里插入图片描述
put和take是阻塞方法,所以在实际的编程中使用阻塞队列的话,其实更多的还是使用者一组。

至于阻塞和唤醒的原因,其实就是当put 队列满了之后,await 阻塞,队列如果取数据的话,就自动唤醒使用notFull.signal。
同理take()也是一样的,如果获取的元素队列是空,那么就会等待。如果有元素入队列,notEmpty.singal()
在这里插入图片描述

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

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

相关文章

全球6G发展大会开幕,为什么我们需要6G

2023全球6G发展大会 由中国IMT-2030&#xff08;6G&#xff09;推进组、中国通信学会、重庆两江新区管理委员会联合主办的2023全球6G发展大会今天在重庆两江新区明月湖成功开幕&#xff0c;开幕式上发布了《6G网络架构展望》《6G无线系统设计原则和典型特征》白皮书。 《6G网络…

有向图的拓扑序列(拓扑排序)

给定一个 n 个点 m 条边的有向图&#xff0c;点的编号是 1 到 n&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出 −1。 若一个由图中所有点构成的序列 A 满足&#xff1a;对于图中的每条边 (x,y)&a…

现货白银简单介绍

在贵金属投资领域&#xff0c;现货白银是当前国际上最为流行、交投最为活跃的白银投资方式&#xff0c;其交易市场遍布全球&#xff0c;包括伦敦、苏黎世、纽约、芝加哥及香港等主要市场&#xff0c;是一种以杠杆交易和做市商的形式进行的现货交易。 现货白银可以说是当下交易模…

对String类的深入理解

String类&#xff1a; String类相信大家对这个类并不陌生&#xff0c;这就是我们熟悉的字符串类型&#xff0c;但是我们一开始只知道它是用来定义字符串的&#xff0c;并不知道它的底层原理&#xff0c;这里我们就来简单的分析一下String的底层原理&#xff0c;首先我们来看一下…

Django回顾 - 6 Ajax

【1】Ajax 定义&#xff1a; 异步Javscript和XML 作用&#xff1a; Javascript语言与服务器(django)进行异步交互&#xff0c;传输的数据为XML&#xff08;当然&#xff0c;传输的数据不只是XML,现在更多使用json数据&#xff09; 同步交互和异步交互&#xff1a; 1、同步交互&…

IO多路复用(新)

1.前景回顾 无论是阻塞IO还是非阻塞IO&#xff0c;用户应用在一阶段都需要调用recvfrom来获取数据&#xff0c;差别在于无数据时的处理方案&#xff1a; 如果调用recvfrom时&#xff0c;恰好内核没有数据&#xff0c;那么阻塞IO会使用户进程阻塞&#xff0c;非阻塞IO使CPU进行空…

Python通过 psd-tools 解析 PSD 文件

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com PSD&#xff08;Photoshop Document&#xff09;是Adobe Photoshop软件中使用的图像文件格式&#xff0c;包含图层、通道、蒙版等信息。在Python中&#xff0c;我们可以使用 psd-tools 库来解析和处理PSD文件。本…

180天Java从入门到就业-Day04-01Java程序流程控制介绍、Java分支结构if语句

1.程序流程控制介绍 1.1 流程控制结构介绍 流程控制语句是用来控制程序中各语句执行顺序的语句,可以将语句组合成完成一定功能的逻辑模块。 一个程序会包含三种流程控制结构:顺序结构、分支结构、循环结构 顺序结构在没有使用程序流程控制语句(if-else语句、switch-case语…

人体姿态估计算法

人体姿态估计算法 1 什么是人体姿态估计2 基于经典传统和基于深度学习的方法2.1 基于经典传统的人体姿态估计算法2.2 基于深度学习的人体姿态估计算法OpenPoseAlphaPose (RMPE) 3 算法应用4 Paper 人体姿态估计在现实中的应用场景很丰富&#xff0c;如下 动作捕捉&#xff1a;三…

AI Pika 生成进击的巨人动漫分镜案例

背景介绍 Pika 是一个使用 AI 生成和编辑视频的平台。它致力于通过 AI 技术使视频制作变得简单和无障碍。 Pika 1.0 是 Pika 的一个重大产品升级&#xff0c;包含了一个新的 AI 模型,可以在各种风格下生成和编辑视频,如 3D 动画&#xff0c;动漫&#xff0c;卡通和电影风格。…

一篇文章带你详细了解C++智能指针

一篇文章带你详细了解C智能指针 为什么要有智能指针内存泄漏1.什么是内存泄漏&#xff0c;它的危害是什么2.内存泄漏的分类3.如何避免内存泄漏 智能指针的使用及原理1.RAII2.智能指针的原理3.auto_ptr4.unique_ptr5.shared_ptr6.weak_ptr 为什么要有智能指针 C引入智能指针的主…

QGraphicsView实现简易地图7『异步加载-多瓦片-无底图』

前文链接&#xff1a;QGraphicsView实现简易地图6『异步加载-单瓦片-无底图』 前一篇文章提到的异步单瓦片加载&#xff0c;是指线程每准备好一个瓦片数据后&#xff0c;立刻抛出信号让主线程加载。而本篇异步多瓦片加载是指线程准备好所有瓦片数据后&#xff0c;一起抛出信号让…