队列总结(五)LinkedBlockingQueue

并发队列之无界阻塞队列LinkedBlockingQueue 目录`LinkedBlockingQueue` 概述`LinkedBlockingQueue` 源码`LinkedBlockingQueue` 数据结构(单链表)`LinkedBlockingQueue` 主要属性 LinkedBlockingQueue 概述 LinkedBlockingQueue 来自于 jdk 1.5 的 JUC 包,是一个支持并发操作的有界阻塞队列,底层数据结构是一个单链表 作为有界队列,容量范围是{1, Integer.MAX_VALUE},可以指定容量,如果未指定容量,则默认容 阅读详情

LinkedBlockingQueue

阻塞队列BlockingQueue接口的常用实现之一,基于链表的可选(可手动指定队列长度)有界阻塞队列

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

构造方法,通过设置属性capacity来限制队列长度,默认容量为Integer.MAX_VALUE。并用属性count 来记录队列当前元素个数。


    /** 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
     * 头结点的item(实际入队元素)始终为null
     */
    transient Node<E> head;

    /**
     * Tail of linked list.
     * Invariant: last.next == null
     * 尾结点的next (后继节点)始终为null
     */
    private transient Node<E> last;
    
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        // 注意这里,构造了链表的头尾节点,都指向一个属性item为null的伪节点
        last = head = new Node<E>(null);
    }

    /**
     * Linked list node class
     */
    static class Node<E> {
        E item;

        Node<E> next;

        Node(E x) { item = x; }
    }

锁相关,两组锁,分别对应(take,poll)与(put,offer)操作

    /** 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();

对比ArrayBlockingQueue可以发现,ArrayBlockingQueue中只使用了一把锁,添加和移除共用这把锁进行并发控制,所以这两个操作是互斥的,不能同时进行。而LinkedBlockingQueue内部分别使用了takeLock 和 putLock 对并发进行控制,添加和删除操作可以同时进行,所以吞吐量大大提高了。也因此,LinkedBlockingQueue在大多数并发应用程序中的可预测性能较差。

至于LinkedBlockingQueue的实现原理图与ArrayBlockingQueue是类似的,除了对添加和移除方法使用单独的锁控制外,两者都使用了不同的Condition条件对象作为等待队列,用于挂起take线程和put线程。

但是如果没有给LinkedBlockingQueue指定容量大小,其默认值将是Integer.MAX_VALUE,如果存在添加速度大于删除速度时候,有可能会内存溢出,这点在使用前希望慎重考虑。

通过观察入队出队操作来理解两把锁的应用:

    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)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(new Node<E>(e));
            c = count.getAndIncrement();
            // 疑惑点1
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
         // 疑惑点2
        if (c == 0)
            signalNotEmpty();
        return true;
    }
    
    private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

逻辑比较简单,线程入队操作enqueue分析,这里盗用一张图,来看看往队列里依次放入元素A和元素B的过程。之前说过了,队列的头结点为一个item为null的伪节点。
在这里插入图片描述对于offer方法,主要的疑惑点有两点:

  1. 为什么添加完成后要继续唤醒在条件对象notFull上的添加线程?

在ArrayBlockingQueue中,添加完成后,并不会再次检测队列容量是否已满,从而去继续唤醒其他添加线程,这是因为ArrayBlockingQueue的添加操作和移除操作是同一把锁控制,是互斥的,即添加过程中元素个数是不会减少的,所以不存在唤醒其他添加线程的必要。同理,添加完成后也不能唤醒其他添加线程,那样会造成添加线程和消费线程的争抢锁行为,可能导致永远无法消费。

而在LinkedBlockingQueue中,添加和移除操作是可以同时进行,不会互斥的。稍后你会看到,在移除方法中,只有当队列是满的时候才会进行唤醒添加线程,且移除操作也是会自己唤醒其他的移除线程,这就意味着可能进行了多次移除操作,只进行了一次唤醒,所以自我唤醒就是很有必要的了。

那么为什么移除操作仅当队列满的时候才对添加线程进行唤醒呢?一是因为唤醒是需要加锁的,这样做可以减少锁的次数。二是因为正常来讲,当队列满了,此时才有可能有多个添加线程如A,B阻塞,移除线程a唤醒其中一个A后,此时后续自我唤醒的另一个移除线程b假如先工作,经过判断队列不满,不会主动唤醒添加线程B,但是添加线程A的自我唤醒机制会唤醒B,所以整体来说,各司其职,提高了吞吐量。

  1. 为什么要判断if (c == 0)时才去唤醒消费线程呢?

消费线程被唤醒后,在队列中有数据的前提下,会自我唤醒,一直消费,所以c值是一直在变化的,c值是添加完元素前队列的大小,此时c只可能是0或c>0,如果是c=0,那么说明之前消费线程已停止,条件对象上可能存在等待的消费线程,添加完数据后就直接唤醒等待消费线程。如果c>0那么消费线程就不会被唤醒,只能等待下一个消费操作(poll、take、remove)的调用,那为什么不是条件c>0才去唤醒呢?我们要明白的是消费线程一旦被唤醒会和添加线程一样,一直不断唤醒其他消费线程,如果添加前c>0,那么很可能上一次调用消费线程后,数据并没有被消费完,条件队列上也就不存在等待的消费线程了,所以c>0唤醒消费线程得意义不是很大。

至此,学习LinkedBlockingQueue主要可学习的就是对锁的运用,对并发的处理,后续就不再深入了。

    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;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            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;
    }

    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

LinkedBlockingQueue和ArrayBlockingQueue迥异

通过上述的分析,对于LinkedBlockingQueue和ArrayBlockingQueue的基本使用以及内部实现原理我们已较为熟悉了,这里我们就对它们的区别来个小结

  1. 队列大小有所不同,ArrayBlockingQueue是有界的初始化必须指定大小,而LinkedBlockingQueue可以是有界的也可以是无界的(Integer.MAX_VALUE),对于后者而言,当添加速度大于移除速度时,在无界的情况下,可能会造成内存溢出等问题。

  2. 数据存储容器不同,ArrayBlockingQueue采用的是数组作为数据存储容器,而LinkedBlockingQueue采用的则是以Node节点作为连接对象的链表。

  3. 由于ArrayBlockingQueue采用的是数组的存储容器,因此在插入或删除元素时不会产生或销毁任何额外的对象实例,而LinkedBlockingQueue则会生成一个额外的Node对象。这可能在长时间内需要高效并发地处理大批量数据的时,对于GC可能存在较大影响。

  4. 两者的实现队列添加或移除的锁不一样,ArrayBlockingQueue实现的队列中的锁是没有分离的,即添加操作和移除操作采用的同一个ReenterLock锁,而LinkedBlockingQueue实现的队列中的锁是分离的,其添加采用的是putLock,移除采用的则是takeLock,这样能大大提高队列的吞吐量,也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

企业数字化转型的理论指南:构建未来企业的关键策略与实践路径 数字化转型已经成为当今企业发展和市场竞争的核心驱动力,而在这一过程中,企业架构(EA)发挥着至关重要的作用。这本白皮书《世界级企业架构:建立和发展EA能力的领导者方法》提供了深入的理论指导,为企业如何通过企业架构实现有效的数字化转型提供了清晰的路径。本篇文章将从,解读该书的核心内容,揭示企业在数字化转型过程中应该如何通过EA的构建和实施,驱动组织变革与技术创新。 阅读详情

相关推荐

手把手教你用ROS2 Humble给PX4无人机写个“画圆”程序:从C++代码到Gazebo仿真全流程

本文详细介绍了如何使用ROS2 Humble和PX4飞控实现无人机圆形轨迹控制,涵盖从环境配置、Gazebo仿真到C++代码实现的完整流程。通过实战教程,读者将掌握轨迹跟踪仿真的核心技术,包括ROS2与PX4的通信接口、圆形轨迹算法设计及Gazebo仿真调试技巧。

weixin_29056781的博客 257

三招!解决消息队列的数据积压问题

java解决消息队列的积压问题

Luyu08209663的博客 2780

幻尔串口总线舵机控制Python SDK

幻尔串口总线舵机控制Python SDK,可以直接在PC端,不需要借助单片机对舵机进行控制。

daishabby2486的博客 4104

【细谈Java并发】谈谈LinkedBlockingQueue

最近在看concurrent包的知识,看到LinkedBlockingQueue,发现一篇好文推荐给大家。 原文地址:【细谈Java并发】谈谈LinkedBlockingQueue 1、简介 上篇我们介绍了ArrayBlockingQueue的相关方法的原理,这一篇我们来学习一下ArrayBlockingQueue的“亲戚” LinkedBlockingQueue。在集合框架里,想必大家都用过Ar...

颤抖吧腿子 12万+

【Python爬虫】股票信息定向爬取

【Python爬虫】股票信息定向爬取:通过requests-bs4-re技术路线爬虫获取上交所和深交所所有股票的名称和交易信息

qq_39321802的博客 1505

C#中的Chart控件——当数据源很多时可以显示进度条拉动观察,也可以记录到后台文本详细对照

C#中的Chart控件——当数据源很多时可以显示进度条拉动观察,也可以记录到后台文本详细对照下面进行详细步骤说明,感兴趣的同学可以看一下: 1简单示例(一条线): 2.数据源随着时间不断变化,进度条变化 。多条线: 3.看在整段时期内的数据大体图(消去或出现)进度条: 4.后台将时刻记录变化记录到一个txt文本里以供详细校验: 话不多说上码: using System; using Sys...

帅boy的博客 4309

阻塞队列-LinkedBlockingQueue

LinkedBlockingQueue是一个基于链表实现的阻塞队列,默认情况下,该阻塞队列的大小为Integer.MAX_VALUE,由于这个数值特别大,所以 LinkedBlockingQueue 也被称作无界队列

wssc63262的博客 6221

特斯拉 Model 3解析(八)全面拆解特斯拉自动驾驶大脑:AP控制器硬核技术解析

算力极限:双FSD = 144 TOPS,专为AI推理优化。存储架构:LPDDR4 + NAND + NOR,保证高速/可靠启动。通信架构:以太网传视频,CAN传控制,TSN保障实时性。感知输入:多路摄像头 + 高速SerDes,满足全景感知。电源设计:功率模块+被动元件海洋,稳定性优先。功能安全:双芯片冗余 + GPS + 紧急呼叫,容错率极高。🎯 在我看来,特斯拉AP控制器就是一台“车载超算”,它在电源设计、存储架构和功能安全上远超一般新势力方案。

weixin_43199439的博客 1445

LinkedBlockingQueue类中的take方法执行后不释放内存问题记录

在项目中,用到了LinkedBlockingQueue,生产者和消费者分别在两个不同的线程中。 在程序运行过程中,发现队列在一段时间后,无法再向其中生产数据,感觉很困惑。 在网上找了一段时间,都没有找到有效解决的方法 在消息生产过程中,会产生积压,但是按理说take是从队首移除,那么移除后应释放内存才对 ,但是好久好久都没有释放,并且我用System.gc()释放,也是一样不行 偶然发现我...

Eddie-Wang的博客 3813

避坑指南:在Linux服务器上用Conda安装antiSMASH 5.2.0(解决Jinja2报错和Matplotlib权限问题)

本文详细介绍了在Linux服务器上使用Conda安装antiSMASH 5.2.0的避坑指南,重点解决了Jinja2报错和Matplotlib权限问题。通过创建专用conda环境、安装指定版本依赖包和配置缓存目录,帮助用户顺利完成antiSMASH的本地部署,适用于批量处理基因组分析任务。

weixin_26911099的博客 316

16、AI与无人机助力紧急器官移植及医疗物资运输

本文探讨了AI与无人机(UAV)集成模型在紧急器官移植和医疗物资运输中的应用。传统运输方式受限于交通、时间和环境因素,而基于图神经网络(GNN)的AI-UAV系统可实现高效、智能、全天候的医疗物资配送,具备实时温湿度监控、自动导航和适应复杂地形等优势。文章分析了该模型在血液、器官运输中的关键作用,展示了其在澳大利亚、中国、美国等国的实际应用案例,并通过表格与流程图说明运作机制。同时,文章还探讨了技术、法规和社会层面的挑战及应对策略,展望了未来技术融合、应用拓展和国际合作的发展趋势,凸显其在全球医疗物流中的广

lll78的博客 102

LinkedBlockQueue 知根知底

LinkedBlockingQueue 是一个可选有界阻塞队列,有界指的是队列存在一个最大容量;阻塞指的是如果队列已经满了,想要往队列继续添加元素的话,那么这个操作将会被暂停,直到队列中有空位才会继续完成添加操作。如果队列已经为空,想要从队列中获取元素,那么这个操作将会被暂停,直接队列中存在元素才会继续完成获取操作。实现方式不同:LinkedBlockingQueue 是基于链表实现的队列,而 ArrayBlockingQueue 是基于数组实现的队列。

Java技术攻略的博客 1283

Kimodo 入门教程(一)英伟达开源人形机器人动捕数据集训练运动学动作扩散模型

Kimodo是一个基于700小时商用光学动作捕捉数据集训练的运动学动作扩散模型,能够生成高质量的人体和机器人3D动作。该模型支持通过文本提示和多种约束条件(如关键帧、末端执行器位置等)进行控制,并提供数字人类和类人机器人两种模型变体。安装方式包括软件包安装和源代码安装两种,需要配置HuggingFace令牌以访问文本编码器。推荐使用GPU加速,最低需要17GB显存。项目提供命令行生成和交互式演示两种使用方式,演示程序支持时间轴编辑和实时3D可视化。模型分为商用友好版和受限版,其中SMPL-X版本需要额外申请

2506_90492529的博客 1934

阻塞队列之五:LinkedBlockingQueue

一、LinkedBlockingQueue简介   LinkedBlockingQueue是一个使用链表完成队列操作的阻塞队列。链表是单向链表,而不是双向链表。采用对于的next构成链表的方式来存储对象。由于读只操作队头,而写只操作队尾,这里巧妙地采用了两把锁,对put和offer采用putLock,对take和poll采用takeLock,即为写锁和读锁,这两个锁实现阻塞(“two lock ...

weixin_30284355的博客 80

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java多线程(五)——线程池任务队列LinkedBlockingQueue

这篇文章继续来介绍线程任务队列——LinkedBlockingQueue,LinkedBlockingQueue是基于链接节点的阻塞队列,它是线程安全的。 LinkedBlockQueue是在jdk1.5之后出现的,先看看它的所继承或实现的超类的关系图。 再看看LinkedBlockingQueue从超类里面所继承过来的方法,最主要的是看AbstractQueue和BlockingQueue 直接看看BlockingQueue接口里面的方法。 public interface BlockingQueue

余韵啊 1216

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

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

JUC(五)-阻塞队列-LinkedBlockingQueue源码分析

JUC、LinkedBlockingQueue源码分析

~奇思妙想的王多鱼的博客 233

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

死磕java concurrent包系列(五)基于AQS的条件队列把LinkedBlockingQueue“扒光”

LinkedBlockingQueue的基础 LinkedBlockingQueue是一个基于链表的阻塞队列,实际使用上与ArrayBlockingQueue完全一样,我们只需要把之前烤鸡的例子中的Queue对象替换一下即可。如果对于ArrayBlockingQueue不熟悉,可以去看看https://juejin.im/post/5c0f79f3f265da61561f1bec LinkedBl...

weixin_33862514的博客 273

线程池的三种队列区别:SynchronousQueue、LinkedBlockingQueue 和ArrayBlockingQueue

使用方法: private static ExecutorService cachedThreadPool = new ThreadPoolExecutor(4, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.MILLISECONDS, new SynchronousQueue&lt;&gt;(), r -&gt; ne...

无名后生 5万+

LinkedBlockingQueue

一、定义 LinkedBlockingQueue继承自AbstractQueue,实现了BlockingQueue,Serializable接口。 LinkedBlockingQueue是一个基于已链接节点的,范围任意的blocking queue 此队列按FIFO(先进先出)排序元素 新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素 链接队列的吞吐量通常要高于基于数组的对列(ArrayBlockingQueue),但是在大多数并发应用程序中,其可预知的性能要低 可选的容量范围

雪雪 3万+

队列 LinkedBlockingQueue

1 api java.util.concurrent包下的新类。LinkedBlockingQueue 就是其中之一,是一个阻塞的线程安全的队列,底层采用 链表 实现。 LinkedBlockingQueue 构造的时候若没有指定大小,则默认大小为 Integer.MAX_VALUE ,当然也可以在构造函数的参数中指定大小。LinkedBlockingQueue 不接受null。 添加元素的方法 ...

猎人在吃肉 1863

Java队列之阻塞队列LinkedBlockingQueue

LinkedBlockingQueue是BlockingQueue中的其中一个,其实现方式为单向链表,下面看其具体实现。(均为JDK8) 一、构造函数 在LinkedBlockingQueue中有三个构造函数,如下图, 1、LinkedBlockingQueue() 这是一个无参的构造函数,其定义如下, public LinkedBlockingQueue() { this(Integer.MAX_VALUE); } 在这个构造函数中调用了有参的构造函数,传入的整型

沧海龙腾LV的博客 880

LinkedBlockingQueue队列介绍及使用

LinkedBlockingQueue队列介绍及使用

刘大猫 1124

[java队列]——LinkedBlockingQueue

[java队列]——LinkedBlockingQueue回顾ArrayBlockingQueueLinkedBlockingQueue介绍LinkedBlockingQueue内部实现基本属性构造方法非空非满条件通知入队出队LinkedBlockingQueue结论与ArrayBlockingQueue比较 回顾ArrayBlockingQueue 之前介绍过[java队列]——ArrayBlo...

way2016的博客 514

深度解析阻塞队列LinkedBlockingQueue

前言 关于阻塞队列的使用,其实之前的文章已经提到过:三种方式实现生产者-消费者模型,最后一种方式就是用阻塞队列实现的。仔细观察会发现,前两种也是在用wait/notify和ReentrantLock/Condition模拟阻塞队列。 LinkedBlockingQueue是阻塞队列中的一种,见名知意,由链表实现的阻塞队列。 ...

Sicimike的博客 2413

基于LinkedBlockingQueue实现的内存队列

目录 描述 应用场景:redis的双写数据一致性 基于LinkedBlockingQueue实现的内存队列demo 配置线程池 项目启动即开始等待消费队列中的消息(模拟生产消息) 描述 如果在消息处理的时候特别费时间,这个时候如果有新的消息来了,就只能处于阻塞状态,造成用户等待。这个时候在项目中引入队列是十分有必要的。当我们接受到消息后,先把消息放到队列中,然后再用新的线程进行处理,这个时候就不会有消息的阻塞了。 首先,我们来看看基于内存的队列。在Java的并发包中已经提供了Blocking

Seven71111的博客 1260

阻塞队列ArrayBlockingQueue与LinkedBlockingQueue

本章主要介绍BlockingDeque和它的实现类ArrayBlockingQueue与LinkedBlockingQueue。

weixin_46425661的博客 1355
上一篇: 队列总结(四)ArrayBlockingQueue
下一篇: 队列总结(六)DelayQueue
MrMoving
博客等级 码龄10年 54粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值