LinkedBlockingQueue多线程安全的保障 —————— 开开开山怪

喜提JDK的BUG一枚!多线程的情况下请谨慎使用这个类的stream遍历。 前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候,看到一个 pr,虽说是在 RocketMQ 的地盘上发现的,但是这个玩意吧,其实和 RocketMQ 没有任何关系。纯纯的就是 JDK 的一个 BUG。我先问你一个问题:LinkedBlockingQueue 这个玩意是线程安全的吗?这都是老八股文了,你要是不能脱口而出,应该是要挨板子的。答案是:是线程安全的,因为有这两把锁的存在。但是在 RocketMQ 的某个场景下,居然稳定复现了 LinkedBlockingQueue 线程不安全的情况。先 阅读详情

LinkedBlockingQueue多线程安全的保障

相信看过我的ArrayBlockingQueue的博客,对于我们分析LinkedBlockingQueue会有一定的帮助,这两个阻塞队列也可以作为我们阻线程池中阻塞对列的选择。

LinkedBlockingQueue采用的是带头节点链表的数据结构。
我们来通过这个类的成员后具体的代码来进行分析LinkedBlockingQueue所采用的实现多线程安全的手段是什么。

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    //用来表示链表的容量大小,如果用户不进行设置的话,那么 capacity = Integer.MAX_VALUE   
      private final int capacity;
   //这个AtomicInteger 类中有一个重要的成员private volatile int value;
   //采用这个 AtomicInteger 中的value进行记录当前元素的数量,
   // AtomicInteger 中对value值进行更改的时候,采用的是CAS进行更改,
   //所以在 LinkedBlockingQueue中调用AtomicIntege的方法虽value进行更改的话是可以保证线程安全的,因为1.value采用类volatile保证了其值的可见性,
   //2是因为采用CAS的形式对其值进行更改,保证其原子性。 
      private final AtomicInteger count = new AtomicInteger();
   //用来记录头节点    
      transient Node<E> head;
   //用来记录尾结点
      private transient Node<E> last;
   //对于LinkedBlockingQueue来说我们采用的是两个ReentrantLock 的对象
   //分别为takeLock 和 putlock,将添加和拿出的操作进行区分。
   //notEmpty 为 takeLock的阻塞队列。
   //notFull  为 putlock的阻塞队列
      private final ReentrantLock takeLock = new ReentrantLock();
      private final Condition notEmpty = takeLock.newCondition();
      private final ReentrantLock putLock = new ReentrantLock();
      private final Condition notFull = putLock.newCondition();

从上边的成员的介绍,你们可能知道了,关于记录阻塞队列中的元素个数,我们知道是线程安全的因为采用了Volitile和CAS,才保证的count个数具有准确定,
但你们一定很迷惑,为什么head和last这两个成员没有任何的修饰,虽然count的准确性保证了,但对于头节点和尾结点的准确性是如何保障的?

我们都知道在进行删除或添加的时候,都需要对头节点和尾结点进行更改
那我们举个例子,我们采用普通的链表的删除和添加的方法

当一个线程想要删除的时候,那么会将head的值先copy一份,然后当线程的时间片段到了,而此时刚好有个线程想要添加,那么此时这个线程会先将tail的值copy一份,然后时间片段到了,想要删除的线程抢占到cpu资源开始执行删除操作,那么将会使头节点的后驱节点设为null,那就说明tail这个节点不会再被引用了,然后当删除线程进行删除完后,添加节点的线程继续添加,因为添加线程已经将tail的值copy一份,所以线程还是一句当前的tail的值进行添加,那么这个tail还是没有删除前的那个tail,此时线程添加的时候,将会添加到tail的后面,那么此时这个阻塞队列就会成为这个局面,由于tail节点不再被引用导致添加的新节点也不会被引用。导致真正内存中的队列只剩下一个头节点,把本来应该要加入的新节点丢失了。

但LinkedBlockingQueue中采用的节点的删除法是将头节点的后驱节点中中的内容进行清除,然后将头节点删除,将头节点的后驱节点作为删除后的头节点,这样的话,对于就不会造成上述所说的节点缺失的问题,
在这里插入图片描述

这个问题是解决了,但是对于不同的线程来说,都保存的是head或者last的复制本,并且也是对其复制本进行操作,会带来一个问题就是最终内存中的head和last的值回因为多线程的问题,导致内存中的head和last不准确

那么LinkedBlockingQueue采取的措施是什么呢。
针对上边的成员来说,我们看到有两个Reentrantlock锁,一个是takelock一个是putlock,那么LinkedBlockingQueue就是采用这两个锁来保证线程安全的。
我们来看看添加操作和删除操作,只展示部分代码说明问题

public boolean offer(E e) {
            final ReentrantLock putLock = this.putLock;
        putLock.lock();//采用的是putlock
        try {
            if (count.get() < capacity) {
                enqueue(node);
                c = count.getAndIncrement();
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
  }
put()
 public void put(E e) throws InterruptedException {
 	final ReentrantLock putLock = this.putLock;
 	final AtomicInteger count = this.count;
        putLock.lockInterruptibly();//采用的是putlock
         while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }

我们从上边的方法就可以看出来,对于添加操作,采用的是putlock锁,
那不用说对于删除操作采用的是takelock,
我们再来看put和offer两个方法调用的核心方法enqueue(node);
发现这个方法只有一行有效代码,并且只对last节点进行了操作

private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

poll和take方法代用的核心方法我们同样发现了,只用了head节点,或者说只对head节点进行操作。

private E dequeue() {
               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;
    }

那我们来总结一下成员head和last为什么没有任何关于线程安全的修饰,同样可以保证多线程安全呢
因为对于LinkedBlockingQueue来说,添加操作采用了putlock锁来保证添加操作的同步,同时又因为添加操作只是对last进行修改,那可以说明last这个变量具有准确性
。同样删除操作采用takelock锁才保证删除操作的同步,同时删除只对head变量进行操作,所以head也是具有准确性的。

链表类型的有界或无界阻塞线程安全队列-LinkedBlockingQueue(FIFO)和LinkedBlockingDeque 链表类型的无界阻塞队列 阅读详情

相关推荐

LinkedBlockingQueue使用场景有哪些

是一个功能强大且灵活的阻塞队列,具有线程安全、阻塞操作、先进先出顺序、多生产者多消费者支持等特点。它非常适用于生产者-消费者模式、线程池任务队列、事件处理系统等场景,尤其是在需要线程间高效数据交换时。生产者-消费者模式:利用实现线程间数据交换。线程池任务队列:用于线程池中的任务调度。事件处理队列:处理异步事件,适用于事件驱动架构。限流处理:限制操作频率,避免过载。任务队列:适用于需要并行处理多个任务的场景。

有时间就写写代码 1281

java数据结构之LinkedBlockingQueue

java数据结构之LinkedBlockingQueue的线程安全保证

冰血ANG的博客 2239

【项目实战】并发编程之Java集合框架中的一个线程安全的队列实现 —— LinkedBlockingQueue入门介绍

LinkedBlockingQueue是Java集合框架中的一个线程安全的队列实现 它可以用于在多个线程之间传递数据。

奶爸的编程之路,也就一周冷个三天 905

java多线程之线程安全队列LinkedBlockingQueue

最近公司在开发项目中,需要通过excel批量上传将商品加入购物车,如果excel中商品过多,用户访问比较多的情况下,给服务器造成的压力极大,稍有不慎,极容易造成服务器崩溃,所以引入队列(实现生产者、消费者模式),保证服务器正常运行。 1、什么是队列Queue 简而言之:队列是一种数据结构,...

3033

IMU、INS、GNSS与组合导航:从原理到工程实践全解析

在自动驾驶、无人机和机器人等智能系统的定位方案中,IMU、INS、GNSS和组合导航常被混为一谈,但它们在硬件层级、数据特性和误差机制上截然不同。IMU作为惯性测量单元,以百赫兹以上频率输出加速度和角速度;INS基于IMU完成姿态、速度与位置的递推解算,却因积分累积误差而随时间漂移;GNSS提供绝对位置,但易受遮挡和干扰。组合导航通过卡尔曼滤波将两者融合,实现短期不飘、长期不偏的连续定位,这正是高精度导航系统的技术底座。工程落地时,从IMU标定、Allan方差分析到相机/LiDAR与IMU外参标定,再到松耦

weixin_29193259的博客 378

PyCharm旧版本下载地址

快捷找到PyCharm历史版本下载地址

piaopiaolanghua的专栏 4429

每天学习一点点之 Java 七大阻塞队列之 LinkedBlockingQueue(理解线程安全)

本文简要分析了的写、取、容量查询、遍历四种操作的源码,发现写和取作为更新类操作,是可以保证线程安全的,但是查询容量在多线程环境下返回的结果并不一定可靠,而遍历操作同样作为查询类操作却是可以保证遍历期间数据的准确性。本文也是基于对的写、取、查询三类操作的源码分析表达一个个人观点我们讨论线程安全于不安全的时候,一定要注意共有变量的使用场景,忽略这个场景去讨论所谓的线程安全与不安全是没有意义的,就好比和我们总会说它们是“线程安全”的List但真的在任何场景下它们的“线程安全”都是一样的嘛?......

【欢迎关注公众号:冬瓜白】 1273

ovs实现lb负载均衡

负载均衡器的实现原理是通过硬件或软件设备将客户端访问流量根据转发策略分发到多个服务器或设备上,以确保系统的负载均衡。二层负载均衡‌:使用虚拟MAC地址方式,根据OSI模型的二层进行负载均衡。‌四层负载均衡‌:基于IP+端口的负载均衡,主要分析IP层及TCP/UDP层,实现四层负载均衡。‌七层负载均衡‌:从应用层开始,根据虚拟的URL或IP、主机名接收请求,再转向相应的处理服务器。

bobi的博客 951

LinkedBlockingQueue 怎么实现的线程安全

实现线程安全的关键在于使用了锁和条件变量来控制对队列的访问。下面是 LinkedBlockingQueue内部使用了一个锁来控制对队列的并发访问。具体来说,它使用了两个不同的锁,一个是putLock,用于;另一个是takeLock,用于。这种分离锁的设计可以。使用了两个条件变量notFull和notEmpty来实现阻塞式的添加和移除元素操作。notFull;notEmpty。条件变量的使用使得线程可以在条件不满足时等待,在条件满足时被唤醒,从而实现了阻塞式的操作。

WaaTong的博客 980

别再纠结了!手把手教你根据项目需求选对目标检测框架(MMDetection/Detectron2/YOLOv8实战对比)

本文详细对比了三大目标检测框架MMDetection、Detectron2和YOLOv8的适用场景与实战技巧,帮助开发者根据项目需求精准选型。从毕业设计到工业落地,分析不同场景下的核心需求与框架优势,提供决策树和避坑指南,确保技术选型与项目目标高度匹配。

weixin_30342209的博客 553

RK3588嵌入式开发:从零构建Rockit多媒体处理引擎的实战指南

本文详细介绍了如何在RK3588嵌入式平台上从零构建Rockit多媒体处理引擎的实战指南。涵盖开发环境搭建、工具链配置、源码编译流程、硬件加速优化及典型问题解决方案,帮助开发者高效利用RK3588的NPU和VPU加速特性,实现高性能多媒体处理应用。

mango的博客 186

java并发体系-LinkedBlockingQueue

LinkedBlockingQueue 是 Java.util.concurrent 包下的一个类,它是一个线程安全的、基于链表的、可选有界的队列。它实现了 BlockingQueue 接口,可以在多线程环境下安全地使用。链表实现:LinkedBlockingQueue 是基于链表的数据结构实现的队列,它可以高效地支持插入和删除操作。线程安全:LinkedBlockingQueue 内部使用了锁来保证多线程环境下的线程安全。

Flying_Fish_roe的博客 1748

二阶锥松弛在配电网最优潮流计算中的应用(IEEE33节点配电网最优潮流算例matlab程序)(yalmip+cplex)

文献[7]建立了以支路潮流计算为基础的OPF模型,针对OPF中的非凸性约束,采用SOCR技术将其松弛为二阶锥约束,整个 OPF 模型则被转化为二阶锥规划( Second Order Cone Programming,SOCP)问题,对其求解可以得到全局最优解。其次结合辐射型配电网潮流特点建立支路潮流约束,并考虑配电网中的可控单元,包括分布式电源和离散、连续无功补偿装置,建立其出力约束,该模型为非凸非线性模型;式中,αj,t和βij,t分别为t时段内节点i电压的二次方和支路ij电流的二次方。

weixin_47365903的博客 1万+

Java并发集合之LinkedBlockingQueue使用与原理

LinkedBlockingQueue原理与实现

wlove514的博客 1525

sift、surf、orb 特征提取——三维重建

参考文章:[sift、surf、orb 特征提取及最优特征点匹配] 最近刚刚入门三维重建,在SFM算法的代码中注意到了feature_type( feature extraction method)这个参数,想细致了解一下这几种特征提取算法,在这里做一下笔记。 SIFT 简介 SIFT(Scale-Invariant Feature Transform)——尺度不变特征变换,是一种计算机视觉的特征提取算法,用来侦测与描述图像中的局部特征。 实质上,它是在不同的尺度空间上查找关键点(特征点),并计算出关键点的

bb_sy_w的博客 4362

【报错解决】彻底解决 MySQL 登录报错 ERROR 1045 (28000)

MySQL 报错 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 通常是由于用户名或密码错误、权限不足、服务未启动或配置文件异常导致的。通过检查并确认密码、重置 root 用户密码、授予完整权限、校验配置文件以及确保 MySQL 服务正常运行,可以彻底解决该问题。掌握这一流程不仅能够恢复 root 登录,还能保障数据库的安全性与稳定性。

博客 1749

BlockingQueue

对于Queue而言,blockingqueue是主要的线程安全的版本,具有阻塞特征,可以允许添加、删除元素时被阻塞,直到成功为止,blockingqueue相对于Queue而言增加了两个方法put/take BlockingQueue接口 属于并发容器的接口,在java,util.concurrent包路径下 BlockingQueue不接受null元素,尝试通过add/put、offer等添加一个null元素时,某些实现上会抛出nullpointExecption问题 BlockingQueu

iiiiiiiiiooooo的博客 581

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

LinkedBlockingQueue

是 Java 中一个线程安全的,它位于包中,底层使用实现。它被广泛用于生产者-消费者模型中,可以在多线程环境下安全地传递和管理数据。putLocktakeLockputtakeputtakeofferpollofferfalsepollnullput(E e)take()offer(E e)falsefalsepoll()nullnullpeek()nullsize()容量 - 当前大小。

weixin_73376914的博客 1188

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

阻塞队列--LinkedBlockingQueue

什么叫线程安全? 线程安全就是每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。 线程安全就是说多线程访问同一代码,不会产生不确定的结果。 并行和并发区别 1、并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑; 2、并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A,交替使用,目的是提高效率。 LinkedBlockingQueue是一个线程安全的阻塞队列,实现了先进先出等特性,是作为.

superviser3000的博客 7221

LinkedBlockingQueue:Java并发编程的强大工具

在Java并发编程中,LinkedBlockingQueue是一个非常重要的数据结构,它是一个线程安全的队列,能够有效地管理和控制多线程环境中的任务。本文将介绍LinkedBlockingQueue的工作原理、应用场景以及简单示例,帮助你更好地理解并发编程中的LinkedBlockingQueue。

隐风的博客 630

Java并发基础:LinkedBlockingQueue全面解析!

LinkedBlockingQueue类是以链表结构实现高效线程安全队列,具有出色的并发性能、灵活的阻塞与非阻塞操作,以及适用于生产者和消费者模式的能力,此外,LinkedBlockingQueue还具有高度的可伸缩性,能够在多线程环境中有效管理数据共享,是提升程序并发性能和稳定性的关键组件。

科技互联网领域深度探索者,乐于分享与写作。 2091

LinkedBlockingQueue和ArrayBlockingQueue的异同

相同: 1、LinkedBlockingQueue和ArrayBlockingQueue都实现了BlockingQueue接口; 2、LinkedBlockingQueue和ArrayBlockingQueue都是可阻塞的队列   内部都是使用ReentrantLock和Condition来保证生产和消费的同步;   当队列为空,消费者线程被阻塞;当队列装满,生产者线程被阻塞; ...

weixin_30580943的博客 1255

LinkedBlockQueue 知根知底

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

Java技术攻略的博客 1283

LinkedBlockingQueue阻塞队列详解,从锁的角度分析LinkedBlockingQueue,LinkedBlockingQueue使用场景(保姆级教程)

java、juc

zhangfuping123456789的博客 1万+

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

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

余韵啊 1215

Java 入门指南:Java 并发编程 —— 并发容器 LinkedBlockingQueue

LinkedBlockingQueue 是 Java 中的一个线程安全的阻塞队列实现。实现了 BlockingQueue 接口,并使用链表作为底层数据结构,可以用于在生产者和消费者之间进行线程安全的数据传输。

热带鱼的技术博客 1452
上一篇: ArrayBlockingQueue 实现多线程安全 —————— 开开开山怪
下一篇: 服务发现框架 —————— 开开开山怪
开开开山怪
博客等级 码龄8年 41粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值