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

T265摄像头插着开机后,需要重新插拔才能正常启动

T265摄像头插着开机后,需要重新插拔才能正常启动

qq_35598561的博客 1077

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

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

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

LTspice基础教程-009.LTspice波形测量

<<==上一篇:LTspice基础教程-008.LTspice PWL设置 >>==下一篇:LTspice基础教程-010. 首篇博客文末提供所有相关资源百度网盘分享链接

gaoyong_wang的博客 4万+

LinkedBlockingQueue 怎么实现的线程安全

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

WaaTong的博客 980

硅光芯片边耦合器综述(一)

摘要:硅光子学在过去几十年间吸引了越来越多的关注,由于其具有诸多优点,如极低的成本、硅的高折射率带来的高集成密度,以及与现有半导体制造工艺的兼容性,被认为是未来日常应用中的一项充满前景的关键技术。在硅光子集成电路中,光互连是一个重要的问题,而在数据中心和光传输系统等应用场景中,光纤到芯片的光互连至关重要。光纤到芯片的光学耦合主要分为两大类:面外耦合(off-plane coupling)和面内耦合(in-plane coupling)。光栅耦合器属于前者,而边耦合器则属于面内耦合。本文主要关注硅光子集成电路

jhbfsoagoodgio的博客 5037

ALSA音频开发实战:如何用snd_pcm_avail避免缓冲区溢出(附完整代码)

本文深入探讨了在ALSA音频开发中,如何利用`snd_pcm_avail`函数有效管理环形缓冲区,从而避免音频数据溢出或下溢。通过实战代码示例,详细解析了该函数在播放场景下的核心作用,即实时查询可写入的帧数,并构建了自适应写入循环以实现稳健的流量控制,确保音频流的平滑与稳定。

weixin_29183637的博客 461

java并发体系-LinkedBlockingQueue

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

Flying_Fish_roe的博客 1748

自动驾驶感知融合算法拆解:从华为MDC案例看前融合与大融合的工程实现

本文深度解析了华为MDC平台在自动驾驶感知融合算法中的前沿工程实践,重点探讨了Camera-Lidar前融合与多传感器大融合的架构设计。通过异构融合架构、数据流优化和场景化应用,华为MDC平台实现了高效、安全的感知能力,显著提升了自动驾驶系统的性能与可靠性。

weixin_29198045的博客 534

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

LinkedBlockingQueue原理与实现

wlove514的博客 1525

28、时间敏感网络中可靠性感知的综合路由与调度

本文针对时间敏感网络(TSN)中现有路由与调度机制主要关注时间触发(TT)流量而忽略其他类型流量效用的问题,提出了一种可靠性感知的综合路由与调度机制。通过将具有不同可靠性和延迟要求的各类流量的联合路由与调度问题形式化为基于优化模理论(OMT)的混合整数规划问题,设计了完整的路由约束与调度约束,并分析了不同类型流量的传输效用,设定了优化目标以提升整体网络性能。实验结果表明,该机制在满足TT流量严格时延与可靠性要求的同时,显著提高了AVB和BE等非关键流量的传输效用,实现了多类型流量的高效协同传输。

hhh00的博客 95

WINCC7.3破解文件

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

BlockingQueue

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

iiiiiiiiiooooo的博客 581

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

LinkedBlockingQueue

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

weixin_73376914的博客 1188

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

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

余额充值