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接口,表示一个线程安全的队列,当队列为空或已满时会阻塞。它是管理并发访问共享资源的有用工具。 阅读详情

相关推荐

华为OD机考 新系统真题 【区分奇偶的数组排序】

华为OD机试 新系统真题【区分奇偶的数组排序】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【区分奇偶的数组排序】多语言题解。

qq_45776114的博客 511

ArrayBlockingQueue

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

weixin_38910645的博客 947

silero-vad 训练自己的数据

silero-vad 训练自己的数据

jacke121的专栏 816

java 常用并发队列- ArrayBlockingQueue

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

Flying_Fish_roe的博客 1651

YOLOv12改进前必看 - YAML模型配置文件详细解读(再也不用担心通道数不匹配) 附网络结构图

YOLOv12的模型配置文件在中,里面包含yolov12的以及的不同大小的模型,本文将详细介绍一下YOLOv12模型文件中各参数的含义。

Limiiiing的博客 3275

华为OD机试 - TLV格式(Python/JS/C/C++ 2025 C卷 100分)

两端通过TLV格式的报文来通信,现在收到对端的一个TLV格式的消息包,要求生成匹配后的(tag,length,valueOffset)列表。和需要匹配的n个tag对应的n行匹配结果,每一行由长度和偏移量组成。第二行:需要匹配的tag数量n(0 < n < 1000)后面N行:需要匹配n个tag值(十进制表示),递增排列。标签10(0A)存在,长度3,valueOffset=2。标签15(0F)长度4,valueOffset=2。标签14(0E)长度2,valueOffset=8。

学Java,找哪吒 2039

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

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

qq_35006660的博客 1186

STM32H743的FDCAN使用方法(1):STM32CubeMX初始化代码生成

STM32H743的FDCAN使用方法(1):STM32CubeMX初始化代码生成。

kevin1499的博客 2565

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

BlockingQueue用法详解之ArrayBlockingQueue

m0_47743175的博客 4306

(持续更新中!)详解【计算机类&面试真题】军队文职考试 ——第一期(真题+解析)| TCP、UDP的区别;程序局部性;ICMP协议及主要功能;重定位所需的计算机硬件

在当今信息化时代,军队中对于计算机专业人才的需求与日俱增。因此,了解面试和复试中常见的问题和要求对于准备应聘者来说至关重要。本文将围绕面试的准备要点、常见问题以及面试技巧进行探讨,并提供一些建议和经验。通过了解面试和复试的准备要点、常见问题以及技巧,读者将能够提升应对能力和成功率。本文为准备军队文职面试和计划考研复试的读者提供了有价值的参考和指导。通过阅读本篇博客,读者将能够获得关于军队文职计算机类面试和计算机考研复试的全面了解,提升自身的应对能力和成功率。

追光者♂:记录、分享、总结、提升,现象级专栏《Python从入门到人工智能》作者,无惧黑暗,坚信曙光 1690

软解调详解

在这个示例中,生成了一个随机的比特序列,将其调制为QPSK信号,添加了高斯白噪声,然后计算了每个比特的LLR,并根据LLR的符号进行了解调判决。软解调是一种基于接收信号的概率估计进行解调的方法。与传统的硬解调相比,软解调不仅考虑了接收信号的幅度和相位信息,还利用统计特性计算每个比特为0或1的概率,从而提高了解调性能。软解调过程中涉及的主要公式包括对数似然比(LLR)的计算公式、信号的概率分布函数以及解调判决公式等。软解调的计算处理流程包括信号接收、预处理、相位估计、LLR计算、解调判决和输出等步骤。

ahah12345678的博客 1226

java并发编程:ArrayBlockingQueue详解

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

初念初恋的博客 2540

ABAP Channels 之 WebSocket 通信实践:用 ABAP Push Channel 打造实时应用

摘要:本文介绍了ABAP Push Channel(APC)技术,它通过WebSocket协议实现ABAP系统与外部客户端的实时双向通信。文章对比了传统HTTP轮询与WebSocket的差异,解释了APC的事件模型及其接口演进(从IF_APC_WS_EXTENSION到IF_APC_WSP_EXTENSION)。通过仓库拣货等实际案例,展示了APC在企业级应用中的价值,如实时监控、协作场景等。文中还详细阐述了ABAP引擎中WebSocket的交互模型,说明其资源优化设计原理,并提供了基础代码示例。该技术解决

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 293

java-ArrayBlockingQueue详解

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

大乐乐 2251

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java阻塞队列:ArrayBlockingQueue

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

常备不懈 704

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

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

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

kk的博客 606

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

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、付费专栏及课程。

余额充值