ArrayBlockingQueue和LinkedBlockingQueue

2023-10-29

ArrayBlockingQueue

ArrayBlockingQueue是一个用数组实现的有界阻塞队列。其是线程安全的,内部通过“互斥锁”保护竞争资源。此队列按照先进先出(FIFO)的原则对元素进行排序。队列的头部是在队列中存在时间最长的元素,队列的尾部是在队列中存在时间最短的元素。

原理和数据结构
说明:
1、ArrayBlockingQueue继承于AbstractQueue,并且它实现了BlockingQueue接口。
2、ArrayBlockingQueue内部是通过Object[]数组保存数据的,即ArrayBlockingQueue本质上是通过数组实现的。ArrayBlockingQueue的大小,即数组的容量是创建ArrayBlockingQueue时指定的。
3、ArrayBlockingQueue与ReentrantLock是组合关系,ArrayBlockingQueue中包含一个ReentrantLock对象(lock)。ReentrantLock是可重入的互斥锁,ArrayBlockingQueue就是根据该互斥锁实现“多线程对竞争资源的互斥访问”。而且ReentrantLock分为公平锁和非公平锁,在创建ArrayBlockingQueue时可以指定,ArrayBlockingQueue默认使用非公平锁。
4、ArrayBlockingQueue与Condition是组合关系,ArrayBlockingQueue中包含两个Condition对象(notEmpty和notFull)。Condition依赖于ArrayBlockingQueue而存在,通过Condition可以实现对ArrayBlockingQueue的更精确的访问。
(1)若某线程(线程A)要取数据时,数据正好为空,则该线程会执行notEmpty.await()进行等待;当其它某个线程(线程B)向数组中插入数据之后,会调用notEmpty.signal()唤醒“notEmpty上的等待线程”。此时,线程A被唤醒得以继续运行。
(2)若某线程(线程H)要插入数据时,数组已满,则该线程会执行notFull.await()进行等待;当其它某个线程(线程I)取出数据之后,会调用notFull.signal()唤醒“notFull上的等待线程”,此时线程H就会被唤醒从而得以继续运行。

函数列表

    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    public ArrayBlockingQueue(int capacity, boolean fair) {

    }

    //创建一个具有给定的(固定)容量和指定访问策略的ArrayBlockingQueue,它最初包含给定collection的元素,并以collection迭代器的遍历顺序添加元素
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {

    }
    //将指定的元素插入此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回true,如果此队列已满,则抛出IllegalStateException
    public boolean add(E e) {

    }

    //将指定的元素插入此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回true,如果此队列已满,则返回false
    public boolean offer(E e) {

    }
    //将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间
    public void put(E e) throws InterruptedException {

    }
    //将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间
    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

    }
    //获取并移除此队列的头,如果此队列为空,则返回null
    public E poll() {

    }
    //获取并移除此队列的头部,在元素变得可用之前一直等待
    public E take() throws InterruptedException {

    }
    //获取并移除此队列的头,在指定的等待时间前等待可用的元素
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {

    }
    //获取但不移除此队列的头,如果此队列为空,则返回null
    public E peek() {

    }
    //返回此队列中元素的数量
    public int size() {

    }
    //返回在无阻塞的理想情况下,此队列能接受的其它元素数量
    public int remainingCapacity() {

    }
    //从此队列中移除指定元素的单个实例
    public boolean remove(Object o) {

    }
    //如果此队列包含指定的元素,则返回true
    public boolean contains(Object o) {

    }
    //返回一个按适当顺序包含此队列中所有元素的数组
    public Object[] toArray() {

    }
    //返回一个按适当顺序包含此队列中所有元素的数组,返回数组的运行时类型是指定数组的运行时类型
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {

    }
    //返回此collection的字符串表示形式
    public String toString() {

    }
    //自动移除此队列中的所有元素
    public void clear() {

    }
    //移除此队列中所有可用的元素,并将它们添加到给定的collection中
    public int drainTo(Collection<? super E> c) {
        return drainTo(c, Integer.MAX_VALUE);
    }
    //最多从此队列中移除给定数量的可用元素,并将它们添加到给定的collection中
    public int drainTo(Collection<? super E> c, int maxElements) {

    }
    //返回一个迭代器
    public Iterator<E> iterator() {
        return new Itr();
    }

源码分析

1、创建

public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

2、添加。
put(E e)方法的源码如下,进行put操作之前,必须获得锁并进行加锁操作,以保证线程安全。加锁后,若发现队列已满,则调用notFull.await()方法,让当前线程陷入等待。直到其它某个线程take某个元素后,会调用notFull.signal()方法来激活该线程。激活之后,继续enqueue(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();
        }
    }
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++;//计数加1
        //若有take()线程陷入阻塞,则该操作激活take()线程,继续进行取元素操作。
        //若没有take()线程陷入阻塞,则该操作无意义。
        notEmpty.signal();
    }

3、取出

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

LinkedBlockingQueue

LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出(FIFO)的原则对元素进行排序。LinkedBlockingQueue可以指定容量,也可以不指定,不指定即为默认长度。

LinkedBlockingQueue内部使用ReentrantLock实现插入(putLock)和取出锁(takeLock),putLock上的条件变量是notFull,即可以用notFull唤醒阻塞在putLock上的线程。takeLock上的条件变量是notEmpty,即可用notEmpty唤醒阻塞在takeLock上的线程。

需要主要的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能被消耗殆尽了。

LinkedBlockingQueue中定义的变量:

/** The capacity bound, or Integer.MAX_VALUE if none */
    private final int capacity;

    /** Current number of elements */
    private final AtomicInteger count = new AtomicInteger();

    /**
     * Head of linked list.
     * Invariant: head.item == null
     */
    transient Node<E> head;

    /**
     * Tail of linked list.
     * Invariant: last.next == null
     */
    private transient Node<E> last;

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            /**
             *当队列满时,调用notFull.await()方法释放锁,陷入等待状态
             *有2种情况会激活该线程:
             *1、某个put线程添加元素后,发现队列有空余,就调用notFull.signal()方法激活阻塞线程
             *2、take线程取元素时,发现队列已满,则其取出元素后,也会调用notFull.signal()方法激活阻塞线程
             */
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            //发现队列未满,调用notFull.signal()激活阻塞的put线程(可能存在)
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            //队列空,说明已经有take线程陷入阻塞,故调用signalNotEmpty激活阻塞的take线程
            signalNotEmpty();
    }

ArrayBlockingQueue和LinkedBlockingQueue的区别

1.队列中锁的实现不同
ArrayBlockingQueue实现的队列中的锁是没有分离的,即生成和消费用的是同一个锁;ArrayBlockingQueue中可以指定锁的公平性,默认为非公平锁;
LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费用的是takeLock,LinkedBlockingQueue中的锁都是非公平锁。

2.在生产或消费时操作不同
ArrayBlockingQueue实现的队列中在生产和消费时,是直接将枚举对象插入或移除的;
LinkedBlockingQueue实现的队列中在生产和消费时,需要把枚举对象转换为Node进行插入或移除,会影响性能。

3.队列大小初始化方式不同
ArrayBlockingQueue实现的队列中必须指定队列的大小;
LinkedBlockingQueue实现的队列中科院不指定队列的大小,但默认是Integer.MAX_VALUE。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ArrayBlockingQueue和LinkedBlockingQueue 的相关文章

  • 动态规划python实现

    什么叫动态规划问题 考虑一个场景 当你有去沙漠旅行 你有一个背包和一些物品 背包有最大承受重量 物品也有重量和价值 而物品种类很多 不可能全都装在背包里 如何去选取价值总量最高的物品组合呢 物品价值表 物品名 价值 water 10 boo
  • Scala基础介绍

    Scala是一门主要以Java虚拟机 JVM 为目标运行环境 并将面向对象和函数式编程有机的结合在一起 因为Scala运行于JVM上 所以Scala可以访问任何Java类库 并且能够与Java框架进行互操作 Scala既有动态语言的灵活简洁
  • Javacv在Windows下正常运行,在Linux上报异常~Could not initialize class org.bytedeco.javacv.FFmpegFrameGrabber

    1 问题描述 今天来分享一个违背Java跨平台的问题 在学习Java第一课老师肯定就是吹嘘Java如何强大 如何跨平台 如何一次编译 到处执行 本文就遇见了在本地windows环境开发没有问题 在Linux的服务器上运行各种异常 这不是有点
  • NodeJs session中间件 及应用(简单的登录与登出)

    session中间件用于为了保存用户数据提供一个session管理器 虽然session中的数据与cookie分开保存 但是session中的数据经过加密处理后默认保存在一个cookie中 因此 在使用session中间件之前必须使用cok
  • layui 中的一些问题

    使用layui 版本号2 4 5 在引入js css之后 1 水平导航栏二级菜单一直不能显示 解决办法 最后发现layui all js引入不能放
  • 有趣的数据结构算法2——快速排序

    有趣的数据结构算法2 快速排序 题目复述 题目分析 具体实现代码 GITHUB下载连接 题目复述 数据排序算法是一类常见算法 其适用范围深入编程的方方面面 常见的数据排序算法有冒泡排序 堆排序 简单选择排序等等 各个适用范围不同 快速排序由
  • 13-Error接口错误处理以及go Module

    Error接口和错误处理 Go语言中的错误处理和其他语言不一样 它把错误当成一种值来处理 更强调判断错误 处理错误 而不是一股脑的catch错误 error接口 Go语言中把错误当成一种特殊的值来处理 不支持其他语言的try catch 捕
  • 【matlab】常用快捷键汇总

    记录自己常用的快捷键 冲冲冲 命令行窗口的常用快捷键 上下光标键 调用Matlab最近使用过的历史命令 便于快速重新执行 编辑器的常用快捷键 Ctrl R 快速注释代码段 拖动鼠标选中需要注释的代码行 Ctrl T 快速取消注释代码段 ht
  • Linux命令之tftp常用参数说明

    tftp 文件传输 实际操作 put 上传 get 下载 mode 文件传输模式 connect 连接 quit 退出 参数 g 下载文件 p 上传文件 l 本地文件名 r 远程主机文件名 常用命令 tftp g r a 10 0 0 1
  • ROS2 驱动思岚G4雷达(ydlidar)- Rviz显示

    记录G4雷达的配置 系统环境为 Ubuntu22 04 配置步骤 1 安装雷达SDK 2 构建 G4 雷达 ROS2 项目工程文件 3 使用Rviz可视化界面显示 1 安装雷达SDK 1 1 安装CMake YDLidar SDK需要CMa
  • 从服务器上复制文件复制不出来的,从远程服务器上复制文件

    从远程服务器上复制文件 内容精选 换一换 问题描述 将在x86平台上经过sh加密的XXX sh x文件复制到基于鲲鹏的服务器中 无法执行 例如 将test sh x从x86平台复制鲲鹏平台云服务器执行 test sh x回显内容如下 问题原
  • 毕设分享javaWeb的网络考试系统的设计与实现

    文章目录 1 项目简介 2 实现效果 3 系统设计 4 关键代码 5 论文概览 6 最后 1 项目简介 Hi 各位同学好呀 这里是L学长 今天向大家分享一个今年 2022 最新完成的毕业设计项目作品 毕设分享javaWeb的网络考试系统的设
  • C#this关键字的四种用法

    用法一 this代表当前类的实例对象 namespace Demo public class Test private string scope 全局变量 public string getResult string scope 局部变量
  • sqli-labs:less-32(宽字节注入)

    宽字节注入 产生原因 1 mysql 在使用 GBK 编码的时候 会认为两个字符为一个汉字 例如 aa 5c 就是一个汉字 前一个 ascii 码大于 128 才能到汉字的范围 2 mysqli real escape string 函数转
  • Helix QAC — 软件静态测试工具

    Helix QAC 是Perforce 公司 原PRQA 公司 产品 主要用于C C 代码的完全自动化静态分析工作 可以提供编码规则检查 代码质量度量 软件结构分析 测试结果管理等功能 Helix QAC 能够全面而正确地发现软件中潜在的问
  • Jmeter系列(33)- 跨平台运行 Jmeter,CSV 文件路径如何设置?

    抛出问题 上一篇文章中详细讲解了 CSV 数据文件设置的用法 https www cnblogs com poloyy 通常 我们编写 调试脚本都是在 Window 机器上 而真正性能测试时 脚本几乎都在 Linux 下运行 使用 CSV
  • 图像处理中 光场(Light Field)简介及理解

    1 光场 Light Field 是一个四维的参数化表示 是空间中同时包含位置和方向信息的四维光辐射场 简单地说 涵盖了光线在传播中的所有信息 光线携带二维位置信息 u v 和二维方向信息 x y 在光场中传递 光场是光线在空间传播中四维的
  • 语法怎么学

    为什么要学非谓语动词 太常见了 非谓语动词可以做句子中 除了谓语的任何成分 是一个非常重要课题 弄懂了这个 就相当于弄懂了语法的90 特别是语法题 怎样学习 我在小破站上查了不少教程 大多数都很好 但不太适合新手 讲得很复杂 我看到一个视频
  • 【Bugly干货分享】手把手教你逆向分析 Android 程序

    很多人写文章 喜欢把什么行业现状啊 研究现状啊什么的写了一大通 感觉好像在写毕业论文似的 我这不废话 先直接上几个图 感受一下 第一张图是在把代码注入到地图里面 启动首页的时候弹出个浮窗 下载网络的图片 苍老师你们不会不认识吧 第二张图是微

随机推荐

  • Pygame实战:Python做一款超好玩的滑雪大冒险小游戏,超会玩【附源码】

    导语 冬日当然要和心爱的人一起去滑雪 徜徉在雪白的世界 浪漫又刺激 唯有爱和滑雪不可辜负 不但风景绝美 而且还超 会 玩 现在还不是时候 但秋天已过半动冬天还会远吗 既然不能现在去滑雪 但是小编可以先让大家身临其境 带大家做一款超好玩的滑雪
  • Kafka3.0.0版本——消费者(RoundRobin分区分配策略以及再平衡)

    目录 一 RoundRobin 分区分配策略原理 二 RoundRobin分区分配策略代码案例 2 1 创建带有7个分区的sixTopic主题 2 3 创建三个消费者 组成 消费者组 2 3 创建生产者 2 4 测试 2 5 RoundRo
  • Latex中一些特殊常用符号的输入

    搞学术的童鞋们很有可能会接触到Latex这种论文格式编辑工具 一般在论文投稿的时候 大多数期刊和会议会给一个Latex模板 要求将你的论文用Latex编辑成 pdf版本 一般的word文字部分是可以直接复制粘贴到latex的 tex文档中的
  • QT子线程中使用主线程的方法

    QT子线程中使用主线程的方法 QMetaObject invokeMethod p setText Q ARG const QString strNum 使用QMetaObject invokeMethod方法将setText函数在主线程运
  • 机器学习——信息熵与信息增益

    问 信息熵越大说明其纯度越高 答 错误 信息熵越小 说明数据集的纯度越高 信息熵是衡量数据集纯度的指标之一 它是对数据集中所有类别出现概率进行加权平均所得到的一个值 信息熵是度量样本集合纯度 不确定度最常用的指标之一 但要注意 信息熵越小
  • 【Mac】mac安装redis客户端 Error: Cask ‘rdm‘ is unavailable: No Cask with this name exist

    1 概述 mac安装redis客户端 rdm 报错如下 lcc lcc brew cask install rdm Updating Homebrew Error Cask rdm is unavailable No Cask with t
  • java——线程池

    一 线程池 线程池可以看做是线程的集合 它的工作主要是控制运行的线程的数量 处理过程中将任务放入队列 然后在线程创建后 启动这些任务 如果线程数量超过了最大数量超出数量的线程排队等候 等其它线程执行完毕 再从队列中取出任务来执行 他的主要特
  • 重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源

    今天这篇文章我们打算来深度解读一下equal方法以及其关联方法hashCode 我们准备从以下几点入手分析 1 equals 的所属以及内部原理 即Object中equals方法的实现原理 说起equals方法 我们都知道是超类Object
  • Android Hierarchy Viewer

    Android的SDK工具包中 有很多十分有用的工具 可以帮助程序员开发和测试Android应用程序 大大提高其工作效率 其中的一款叫Hierachy Viewer的可视化调试工具 可以很方便地帮助开发者分析 设计 调试和调整UI界面 提高
  • 高斯低通频率域滤波

    基本原理 频率域滤波 即将原图像通过傅里叶变换 转换至频率域 在频率域利用该域特有的性质进行处理 再通过傅里叶反变换把处理后的图像返回至空间域 所以 频率域的操作是在图像的傅里叶变换上执行 而不是在图像本身上执行 高斯低通滤波器传递函数表达
  • 跟着 iLogtail 学习设计模式

    设计模式是软件开发中的重要经验总结 Gang of Four GoF 提出的经典设计模式则被誉为设计模式中的 圣经 但是设计模式往往是以抽象和理论化的方式呈现 对于初学者或者没有太多实战经验的开发者来说 直接学习设计模式往往会显得枯燥乏味
  • c++ parse html,c++ - QT parse html to txt file - Stack Overflow

    I think it s always best if you actually attempt at something and post up your code to serve as a starting point But I m
  • 小径

    尽入夏 绕竹篱 已是桃花稀落 笑到西川 此去随所遇 不羡青山不拜仙 园中花草 草木香幽 清风独得朝暮暖 蕲水携来四季春 云岩宫阙 尽是人间 峰峦断却处 本是一脉之水 两侧命不相同 一水之门 几多思量 几多判却 一方玲珑剔透 嬉水无痕 一方藻
  • 计算机网络——第四章

    网络层 主要任务是把分组从源端传送到目的端 为分组交换网上的不同主机提供通信服务 传输单位是数据报 功能 1 路由选择与分组转发 2 异构网络互联 3 拥塞控制 若所有节点都来不及接受分组 而要丢弃大量分组的话 网络就处于拥塞状态 因此要采
  • python数据库-NumPy与Matplotlib库

    NumPy 1 导入numpy库 import numpy as np python中用import导入库 这里的意思是将怒骂朋友作为np导入 通过这样的形式 之后使用numpy相关方法用np使用 2 生成numpy数组 import nu
  • LeetCode--数组类算法:删除排序数组中的重复项 II

    题目 给定一个排序数组 你需要在原地删除重复出现的元素 使得每个元素最多出现两次 返回移除后数组的新长度 不要使用额外的数组空间 你必须在原地修改输入数组并在使用 O 1 额外空间的条件下完成 示例一 给定 nums 1 1 1 2 2 3
  • 梳理webpack

    一 入门 1 项目初始化 新建一个目录 初始化npm npm init 此时会需要填入一些项目的基本描述 webpack是运行在node环境中的 我们需要安装以下两个npm包 npm i D webpack webpack cli 生成no
  • 【mcuclub】扫码枪-(型号:M100(1D)-TTL)(型号:GM861S)

    一 实物图 型号 M100 1D TTL 只能扫描一维条形码 二 原理图 编号 名称 功能 1 VCC 电源正 2 GND 电源地 3 TXD 串口数据发送引脚 接单片机上的RX引脚 4 RXD 串口数据接收引脚 接单片机上的TX引脚 三
  • Unity 处理mono内存(堆内存)泄露问题

    先讲解一下mono特性 一个很重要的信息 mono内存从系统里面申请的内存不会返回给系统 mono内存不足的时候会预申请内存 内存大小不定有可能10m有可能5m 最近优化一个mono内存泄露问题 引起mono一直撑大多数都是内存泄露 要不就
  • ArrayBlockingQueue和LinkedBlockingQueue

    ArrayBlockingQueue ArrayBlockingQueue是一个用数组实现的有界阻塞队列 其是线程安全的 内部通过 互斥锁 保护竞争资源 此队列按照先进先出 FIFO 的原则对元素进行排序 队列的头部是在队列中存在时间最长的