ArrayBlockingQueue 实现多线程安全 —————— 开开开山怪

深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue 本篇我们将一起对 AQS的另一类的应用场景的实现类——阻塞队列(实现生产者/消费者模型的经典方法) 做一次深入浅出的分析。从上面的类图结构和源码的注释分析来看,我总结如下:从上面总结的第一和第二点我们知道,BlockingQueue的接口方法包含四种类型的: 会抛异常的、返回特殊值的、会阻塞的和阻塞有超时的 ;但是按照操作结果大体可分为三种(官方区分的): 插入型的、删除型的、读取型的 。如下面的表格总结(其实也是源码的注释内容):正在上传…重新上传取消下面我们就此表格说明下( 我又多分了一个种类->读取删 阅读详情

主要内容

ArrayBlockingQueue.

这篇博客主要说说ArrayBlockingQueue这个阻塞队列的存储结构以及针对多线程的情况,这个阻塞队列是怎样实现多线程安全的,还有就是一些方法的区别。
其实对于多线程我也是慢慢体会到的,看了源码,看了别人的实现方式,才渐渐懂得怎样处理多线程安全的问题。
ArrayBlocking采用的是数组的存储方式。
希望你们也是如此。

当然在说的过程,采用源码来说是比较有权威的。

ArrayBlockingQueue

ArrayBlockingQueue是采用的是循环数组的形式表达队列,所以在该类中不存在扩容的情况,对于数组的长度来说,根据初始化的参数为标准,类中没有默认的数组长度。
其实对于这个类是怎样来实现多线程安全的呢,从这个类的成员就可以猜想到。

public class ArrayBlockingQueue<E> extends AbstractQueue<E>  implements BlockingQueue<E>, java.io.Serializable {
       final Object[] items;//存放元素的数组
       int takeIndex;//记录元素被取出元素的数组下标
       int putIndex;//记录元素被放入元素的数组下标
       int count;//记录元素的个数
       final ReentrantLock lock;//锁,同样也是保证多线程安全的一个重要因素
       private final Condition notEmpty;
       //notEmpty是当前lock的阻塞队列
       //作用就是采用内部的一个Condition队列来存储想通过put进行添加元素,但由于数组已满而被阻塞的线程。
       private final Condition notFull;
       //notFull是当前lock的另一个阻塞队列
       //作用就是采用内部的Condition队列来存储想通过take进行取出元素,但由于数组为空而被阻塞的线程。
       transient Itrs itrs = null;
}  

刚才说,可以通过成员来推测出这个类采用的什么手段来实现多线程。其实我是看完之后才进行总结的,不是我一开始看的时候就知道。

那我们一步一步来看,
这个类给我们提供了很多添加和取出的方法,而多线程安全的处理当然是在方法上进行处理的。
那为什么说通过成员就可以看出来实现手段呢,
int count 这个成员是用来记录数组中当前拥有的元素的个数,那么通过添加和取出都要对这个变量进行操作,而这个成员并没有采用volitile进行修饰。但是该类采用了一个ReentrantLock lock,那我们就可以得出结论,这个类在所有的方法上都采用lock进行多线程安全的保证。所以对于count来说可以不添加任何有关多线程安全的修饰。

为什么这么说呢,那首先就要对ReentrantLock 有一定的认识,如果不了解请看博客ReentrantLock.相信你就会理解。
因为我们知道一个ReentrantLock 对象对应一个AQS,所以对于ArrayBlockingQueue来说,如果你对一个ArrayBlockingQueue对象进行操作(加入或者取出),那当然对于这个对象来说也只有一个ReentrantLock 对象作为其ArrayBlockingQueue类的成员。
同样是因为在ArrayBlockingQueue类中的加入和取出的方法中都要对其lock进行获取,而lock对于一个ArrayBlockingQueue对象来说只有一个,那么在多线程进行调用加入或者取出的方法的时候,自然会排队进行,因为对于多线程来说,他们认识的是同一把锁lock,当有线程在执行加入或者取出的方法的时候,自然会持有这把锁,那么对于其他线程来说就只能加入到lock锁的等待队列中,等一个线程执行完,释放锁唤醒等待队列中的等待线程的时候,那些等待线程才会去挨个执行他们的操作。

所以对于ArrayBlockingQueue的成员count 没有采用任何的修饰,只是一个普通的变量,因为对于每个线程对ArrayBlockingQueue 对象所执行的操作都是顺序的,所以count具有准确性。

那我们来看ArrayBlockingQueue这个类中给我们提供的操作方法。
不是白看的,你们找这些个方法的一个共同点。你们找完之后,我会慢慢说这些个方法的具体情况。

poll();
 public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();//这里的lock就是ArrayBlockingQueue类中唯一的锁lock。
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
 }
offer(E e)
public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
put(E e)
 public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
take()
 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

找出来了吗?
通过上边的方法,我们也能清楚的看到,每个方法开头都采用了lock.lock,方法的结尾都采用了lock.unlock。
所以可以说对于ArrayBlockingQueue来说,实现多线程的手段比较粗鲁,就整了一个ReentrantLock 对象作为成员,就可以让多线程在方法的执行上做到同步了,同样也就实现类多线程安全。
所以ArrayBlockingQueue类中还有其他成员
final Object[] items;
int takeIndex;
int putIndex;
例如这三个成员,也都没有任何的关于线程安全的修饰,因为一个lock就做到了。

下面我们来说说offer()和put()的区别,take() 和poll() 的区别

put() 和 offer()

说到offer你们可以回看上面的方法,对于offer来说,当一个线程调用offer的时候,如果此时数组已经满员了,那么offer会直接返回false。表示不能够添加
而对于put()来说,同样也是添加,但是就不一样了,当一个线程调用put()的时候,如果此时数组已经满员了,那么你回看上边的方法,你会看到 notFull.await();这样的代码,此时对于调用put方法的这个线程来说,会进行阻塞,而不是像offer一样直接返回false。这个nofull成员我在一开始的时候已经说过了。
其实在这个notfull所带领的阻塞队列中的线程表明都是等待添加的线程,如果你看过我写的博客Condition.你就会知道Condition阻塞队列的原理。然后就等待被唤醒了。

take() 和 poll()。
poll方法可以回看上边的源码,表示取出一个元素,当数组为空时,则直接返回null。
而take方法,倒是和put方法有的一拼,当数组为空时,会执行notEmpty.await();这个代码,表明将调用take方法的线程进行阻塞。但对于区操作,是将操作的这个线程阻塞起来,加入到notEmpty带领的这个阻塞队列中。然后就等待被唤醒了。

其实不管是notEmpty和是notFull,这两个Condition对象都是由lock这一个对象生成的,说明不管是在两个阻塞队列中任何一个阻塞线程,在被唤醒之后都是要进入到lock的等待队列中的。因为不管在什么时候,只有可能是一个线程持有锁。

既然说到了阻塞线程需要被唤醒,何时被唤醒?

对于put来说,表明数组中没有位置,所以无法put,只能阻塞,当一旦有poll操作的时候,poll方法会调用一个dequeue方法这个方法就是将数组中最老的元素进行取出,关键是在最后有一句 notFull.signal();
采用这句进行唤醒在notFull阻塞队列中的线程,被唤醒的线程加入到lock的等待队列中,等待机会执行。

 private E dequeue() {
        final Object[] items = this.items;
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

对于take来说,数组为空时,无法获取,进行阻塞,当一旦有offer操作的时候,offer操作会调用enqueue()方法,进行元素的添加,在最后进行notEmpty.signal();唤醒等待取操作的线程,被唤醒的线程加入到lock的等待队列中,等待机会执行。

 private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

总结:
对于ArrayBlockingQueue类来说,采用实现多线程安全的手法还是比较简单,但同时也是我们可能最常用的一种手段,最主要的还是ArrayBlockingQueue应用了Reentrantlock这个类来进行实现的,还有Condition与Reentrantlock的结合。ArrayBlockingQueue中对元素的添加和删除的本质对循环数组的操作我们都很熟悉,最主要的还是能够理解Reentrantlock和Condition这两个类的原理,这样再回头自己看这个应用这两个类的阻塞队列的实现就会变得简单很多。

【项目实战】并发编程之Java集合框架中的一个线程安全的队列实现 ——BlockingQueue入门介绍 BlockingQueue是一个Java接口,表示一个线程安全的队列,当队列为空或已满时会阻塞。它是管理并发访问共享资源的有用工具。 阅读详情

相关推荐

数学建模小白必看:层次分析法(AHP)从入门到实战(附Python代码)

本文详细介绍了层次分析法(AHP)在数学建模竞赛中的应用,从构建判断矩阵到一致性检验,再到权重计算和实战案例。通过Python代码实现,帮助小白快速掌握AHP的核心技巧,解决评价类难题,提升建模效率。

web39explorer的博客 918

ArrayBlockingQueue

概述 ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列。 线程安全是指,ArrayBlockingQueue内部通过互斥锁保护竞争资源,实现了多线程对竞争资源的互斥访问。 有界是指,ArrayBlockingQueue对应的数组是有界限的。 阻塞队列是指,多线程访问竞争资源时,当竞争资源已被某线程获取时,其他要获取该资源的线程需要阻塞等待;而且ArrayBlockingQueue是按FIFO(先进先出)原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。 ArrayB

weixin_38910645的博客 947

Ubuntu环境下读取罗技G29方向盘信息

Ubuntu环境下读取罗技G29方向盘信息

工大机器人工作室 3463

java 常用并发队列- ArrayBlockingQueue

是一个线程安全的、基于数组的有界阻塞队列。它在初始化时需要指定队列的容量,一旦创建,容量就不能改变。它的实现采用了锁和条件变量来控制并发访问,并提供了线程间的安全通信机制。是 Java 并发编程中一种常用的有界阻塞队列,适合生产者-消费者模型及需要在多个线程之间安全地共享数据的场景。通过显式锁和条件变量,提供了简单而有效的线程间通信机制。尽管它有容量限制和可能的锁竞争问题,但在需要固定容量控制和阻塞操作的场景中,是一个非常实用的选择。是 Java 中常用的并发队列之一,属于阻塞队列的范畴。它是一个。

Flying_Fish_roe的博客 1651

物联网技术-第2章-物联网技术架构

• 物联网具有很强的异构性• 为实现异构设备之间的互联、互通与互操作,物联网需要以一个开放的分层的可扩展的为框架。

m0_73804764的博客 2976

dToF直方图之美

上面视频里如果你能看懂,那么就能理解对于dToF单点的测距问题,对于大部分的描述在AMS和ST里的手册都非常的详细,有疑问的可以后台来找我探讨,今天我们单纯从上层讨论成像的问题。作为dToF算法工程师,应该是对直方图敏感的,直方图的每个Count峰都会表征出问题,因为dToF的输出只有直方图,观察直方图能够逆推出dToF的很多东西,这也是大多数公司不愿意开放直方图和Raw data的缘故。以上是一个小面阵的分辨率,第一张是直方图,后面是基于直方图寻峰出来的成像ToF图,Peak图,以及噪声Noise图。

dream it possible 2995

线程安全的队列-ArrayBlockingQueue源码分析

一,ArrayBlockingQueue源码分析ArrayBlockingQueue是队列的一种,队列的特点嘛,先出先出,然而这种队列是一种线程安全阻塞式的队列,为什么是阻塞式队列?我想...

qq_35006660的博客 1186

从零开始:使用VMOS Pro和Xposed绕过APP环境检测的完整指南

本文详细解析了使用VMOS Pro和Xposed框架绕过APP环境检测的完整技术方案,包括模拟器检测、Root环境检测和Hook框架检测的对抗方法。通过实战案例和代码示例,展示了如何利用Xposed Hook技术拦截检测逻辑,特别针对360加固等保护技术提供了解决方案。

ee345的博客 734

多线程基础(六、二)BlockingQueue用法详解之ArrayBlockingQueue

BlockingQueue用法详解之ArrayBlockingQueue

m0_47743175的博客 4306

手把手教你用89C52单片机驱动0.96寸OLED(IIC接口,附完整代码)

本文详细介绍了如何使用89C52单片机通过I2C接口驱动0.96寸OLED显示屏,包括硬件连接、I2C通信原理、SSD1306驱动芯片的初始化及显存管理技巧,并提供了完整的代码示例和常见问题排查方法,帮助电子爱好者快速掌握OLED显示技术。

weixin_30781433的博客 369

通用抓取————6D位姿估计

这意味着算法在没有物体的先验知识(如 CAD 模型)的情况下,依然能够通过神经网络捕捉物体的外观、形状和几何特征,生成相关的 6D 位姿估计。通过输入物体类别的描述,算法能够推断出该类别物体的位姿和尺度,无需特定物体的 CAD 模型。在这个坐标空间中,物体的位姿和尺度都是已知的,基于这个标准化表示,算法可以对输入图像中的物体进行 6D 位姿和尺寸的估计。○优势: ICP 是一种非常有效的位姿优化算法,特别适合精细调整物体的位姿,确保最终估计的位姿与真实物体的点云或模型高度一致。

具身机器人和通用AI 2380

java并发编程:ArrayBlockingQueue详解

ArrayBlockingQueue 顾名思义是基于数组的阻塞队列。数组是要指定长度的,所以使用 ArrayBlockingQueue 时必须指定长度,也就是它是一个有界队列。它实现了 BlockingQueue 接口,有着队列、集合以及阻塞队列的所有方法。ArrayBlockingQueue 是线程安全的,内部使用 ReentrantLock 来保证。ArrayBlockingQueue 支持对生产者线程和消费者线程进行公平的调度。

初念初恋的博客 2540

反应动力学仿真软件:ChemkinPro_(11).敏感性分析

敏感性分析是化学反应动力学仿真中的一项重要技术,通过对模型参数的变化对模型输出的影响进行评估,可以帮助研究人员优化模型、验证实验数据和评估不确定性。ChemkinPro 提供了强大的敏感性分析功能,可以方便地进行参数选择、仿真设置和结果解读。通过实际案例研究,我们可以看到敏感性分析在燃烧、催化和环境化学等领域的广泛应用,为科学研究和工程应用提供了有力支持。

kkchenjj的博客 1309

java-ArrayBlockingQueue详解

是一个基于数组实现的阻塞队列,它继承自并实现了接口。这个队列在创建时需要指定一个固定的大小,之后这个大小就不能再改变了。当队列满时,如果再有新的元素试图加入队列,那么这个操作会被阻塞;同样地,如果队列为空,那么从队列中取元素的操作也会被阻塞。这种特性使得非常适合作为生产者-消费者模式中的缓冲区。是Java并发编程中一个非常实用的工具类。它提供了线程安全的阻塞队列实现,支持生产者-消费者模式,并允许通过队列的大小来控制系统的流量。在使用。

大乐乐 2251

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Java阻塞队列:ArrayBlockingQueue

`ArrayBlockingQueue`是Java中的一个阻塞队列(Blocking Queue)实现,它是线程安全的,并且基于数组实现。`ArrayBlockingQueue`常用于生产者-消费者模型,在这种模型中,生产者线程负责将元素放入队列,而消费者线程负责从队列中取出元素。

常备不懈 704

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍。内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原理和数据结构ArrayBlockingQueue函数列表ArrayBlockingQueue源码分析(JDK1.7.0_40版本)ArrayBlockingQueue示例 转载请注...

kk的博客 606

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Java进阶篇--并发容器之ArrayBlockingQueue与LinkedBlockingQueue

它提供了put、take等方法来实现元素的添加和移除,并且在队列为空或已满时能够自动阻塞和唤醒相应的线程,以保证生产者和消费者之间的同步和协作。总之,ArrayBlockingQueue 是一种高效、线程安全的数据结构,适用于多线程环境下的生产者-消费者问题,其中生产者线程负责将元素插入队尾,消费者线程负责从队首取出元素进行处理。如果队列不为空,则获取并移除数据,并通知生产者线程。在这个例子中,我们使用了多个生产者和消费者线程,它们同时操作LinkedBlockingQueue,展示了队列的并发性质。

m0_74293254的博客 2434

深入解析Java多线程下 ArrayBlockingQueue

文章目录一.ArrayBlockingQueue 介绍 一.ArrayBlockingQueue 介绍 ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,实现了BlockingQueue接口。 底层以数组的形式保存数据(Object [])。常用的操作包括 add,offer,put,remove,poll,take,peek。 public...

杨海吉 512

阻塞队列之四:ArrayBlockingQueue

一、ArrayBlockingQueue简介 一个由循环数组支持的有界阻塞队列。它的本质是一个基于数组的BlockingQueue的实现。 它的容纳大小是固定的。此队列按 FIFO(先进先出)原则对元素进行排序。 队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。 新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 这是一个典型的“有界缓存区”,固...

weixin_33840661的博客 164

Java多线程——安全线程问题(ConcerrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet、ArrayBlockingQueue)

介绍CopyOnWriteArrayList CopyOnWriteArraySet 基础是CopyOnWriteArrayList,线程安全的Set,不允许重复 ConcurrentLinkQueue 无界的队列,然后是线程安全的Queue ArrayBlockingQueue 是一种线程安全的阻塞的Queue,(生产者、消费者问题) ConcerrentHashMap 一种线程安全的HashMap

m0_57532232的博客 537

JAVA多线程之——ArrayBlockingQueue

ArrayBlockingQueueArrayBlockingQueue队列是BlockingQueue的一个子类.block是阻塞的意思。它是一个阻塞队列,array说明它的底层是基于数组实现。数组是有大小的,所以它是一个有(边)界的队列。综上,ArrayBlockingQueue是一个有界的阻塞队列。 BlockingQueue BlockingQueue接口有三个删除方法和三个添加方法

永不止步 740

第十章 Java多线程--阻塞队列--ArrayBlockingQueue

ArrayBlockingQueue 是基于数组的有界阻塞队列。此集合类保证它的元素按照 FIFO(先进先出)原则进行排序。当构造 ArrayBlockingQueue 实例时,必须指定其容量大小,并且一旦创建就不能更改。

Lin_Miao_09的博客 1210

ArrayBlockingQueue 和 LinkedBlockingQueue

顾名思义:基于数组的阻塞队列。数组是要指定长度的,所以使用时必须指定长度,也就是它是一个有界阻塞队列。(有界是指他的容量大小是固定的,不能扩充容量,在初始化时就必须确定队列大小。它实现了接口,有着队列、集合以及阻塞队列的所有方法。它内部使用来保证线程安全。支持对生产者线程和消费者线程进行公平的调度,默认情况下是不保证公平性的。公平性通常会降低吞吐量,但是减少了可变性和避免了线程饥饿问题。是一个阻塞队列,内部由来实现线程安全,由Condition的await和signal来实现等待唤醒的功能。

qq_24907431的博客 2376

JAVA多线程(二十五)Java多线程之ArrayBlockingQueue容器

1.JAVA多线程(二十五)Java多线程之ArrayBlockingQueue容器 1.1 什么是BlockingQueue   阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是 BlockingQueue 提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。 Blocki...

yuan_xw的专栏 672

多线程(十八、阻塞队列-ArrayBlockingQueue)

ArrayBlockingQueue简介 1、队列的容量一旦在构造时指定,后续不能改变; 2、插入元素时,在队尾进行;删除元素时,在队首进行; 3、队列满时,插入元素会阻塞线程;队列空时,删除元素也会阻塞线程; 4、支持公平/非公平策略,默认为非公平策略。 ArrayBlockingQueue构造 核心构造方法: /** * 指定队列初始容量和公平/非公平策略的构造器. */ public A...

ceoaxv5008的博客 272

ArrayBlockingQueue和LinkedBlockingQueue解析

1. BlockingQueue接口简介 java.util.concurrent 包里的BlockingQueue是一个接口,继承Queue接口,Queue接口继承Collection。 BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下: 抛出异常 特殊值...

傅红雪的专栏 1700
上一篇: Reentrantlock和其搭档Condition —————— 开开开山怪
下一篇: LinkedBlockingQueue多线程安全的保障 —————— 开开开山怪
开开开山怪
博客等级 码龄8年 41粉丝 30原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值