Java 集合 ConcurrentLinkedQueue

简介

ConcurrentLinkedQueue是一个基于链表实现的无界线程安全非阻塞队列,采用先进先出规则,新入队的节点在队列的尾部,获取元素时从队列头部返回。这个类的最显著的特点是,head 和 tail 都不一定指向真实的头结点和尾结点

offer(E e)方法每两次操作入队才会推进一次tail节点,目的是为了减少CAS的调用次数,这是导致tail不一定指向真实尾节点的原因。这直接导致 head 不一定指向真实头结点,tail 也不一定指向真实尾结点,这样设计的目的是为了减少CAS的次数。

poll()方法也是每两次操作出队才会更新一次head节点,获取头部元素时,会把头部元素的item置为null,成功之后会再把头节点指向自己(可以叫冲洗 / 自引用)以达到回收的目的。多线程情况下,另一个线程如果发现头节点被冲洗时,会重新获取头节点然后再次尝试。

使用size()方法获取队列元素总数时效率会比较低,因为size()方法内部是通过遍历链表实现的。只需要判断链表是否为空时,用isEmpty()方法效率更高,因为它只判断头节点是否为null。

代码细节

offer(E e)

代码注释

    public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
			// 下一个节点为空 说明当前节点是尾结点 CAS设置新节点
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
					// 连续插入2个节点 由于插入第一个元素不会更新tail 
					// 因此插入第二个元素的时候会走到else的逻辑 
					// 导致下一轮循环中 p!=t 这时候就会更新tail 
					// 也就是说tail不一定指向真实尾结点
                    if (p != t) // hop two nodes at a time
						// 更新失败了也没关系 因为失败了表示有其他线程成功更新了tail节点
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
			// 由于poll()会把头结点设置为自引用
			// 当出现并发入队出队时就可能出现 p == q 的情况
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
				// 走到这里说明已经从链表上被摘下来了
				// 所以必须回到主链表才能继续执行
				// tail变了优先取tail 否则就取head
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
				// 这里的逻辑和上一个分支类似
				// 也是tail变了优先取tail 否则就取q
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

poll()

    public E poll() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {
                E item = p.item;

				// 若head指向的就是真实头结点 直接出队
                if (item != null && p.casItem(item, null)) {
                    // Successful CAS is the linearization point
                    // for item to be removed from this queue.
					// 插入一个节点后立刻删除节点
					// 此时head指向的不是真实头结点
					// 因此会走到else逻辑取下一个节点
					// 此时就会出现 p != h 的情况
					// 这时候就需要更新head
                    if (p != h) // hop two nodes at a time
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
				// 若下一个节点也是null 说明队列空了
				// 设置自引用 然后直接返回null即可
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
				// 发现自引用 回到外部循环找最新head重试
                else if (p == q)
                    continue restartFromHead;
				// 若当前节点为空节点 不是自引用节点
				// 并且下一个节点不为null 直接让p指向下一个节点
				// 进入下一轮循环处理
                else
                    p = q;
            }
        }
    }

关于 t != (t = tail)

简单理解就是 t 和 tail 都会先入栈,然后才会把 tail 赋值给本地变量 t,因此这个代码实际上是先比较 t 和 tail,然后才把 tail 赋值给 t。通过反编译代码,可以在字节码层面印证这个逻辑,具体见参考链接。

参考链接

ConcurrentLinkedQueue (Java Platform SE 8 )

Java并发编程之ConcurrentLinkedQueue详解-CSDN博客

理解 t != (t = tail)-CSDN博客

JVM 字节码 对照表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/603172.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉坑指南

几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 是在 OpenHarmony 的 kernel_liteos_a 基础上给内核源码加上中文注解的版本.与官方源码按月保持同步,同步历史如下: 2021/10/09 – 增加性能优化模块perf,优化了文件映射模块2021/09/14 – common,extended等几个目录结构和M…

文献速递:深度学习医学影像心脏疾病检测与诊断--基于深度学习的低剂量SPECT心肌灌注图像去噪:定量评估与临床表现

Title 题目 Deep learning–based denoising of low‑dose SPECT myocardialperfusion images: quantitative assessment and clinical performance 基于深度学习的低剂量SPECT心肌灌注图像去噪&#xff1a;定量评估与临床表现 01 文献速递介绍 单光子发射计算机断层扫描&a…

uniapp + vue3 设置 axios proxy 代理,并重写路径

uniapp vue2 设置代理如下&#xff1a; 已生成的项目架构里面找到manifest.json文件&#xff0c;通过源码视图的方式打开文件&#xff0c;在文件中添加一下代码即可完成代理&#xff1a; "h5": {"devServer": {"disableHostCheck": true, //禁…

基于StatefulSet控制器在Kubernetes上部署MySQL一主多从

一、前提--StatefuSet特性 1.1 有状态的节点控制器 -- StatefulSet 及其网络状态 容器的解决方案是针对无状态应用场景的最佳实践&#xff0c;但对于有状态应用来说&#xff0c;就并非如此了。Kubernetes 用 StatefulSet 解决了有状态应用编排的问题&#xff0c;本文我们就来…

GitHub介绍,GitHub如何订阅充值?

一、GitHub介绍 GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持git 作为唯一的版本库格式进行托管&#xff0c;故名Github。 GitHub于2008年4月10日正式上线&#xff0c;除了git代码仓库托管及基本的Web管理界面以外&#xff0c;还提供了订阅、讨论组、…

爬取深圳2024年链家二手房数据,共3000条数据(其他城市也可)

文章目录 专栏导读1.目标2.导入相关库3.获取每个二手房的链接4.获取每个链接中的相关数据5.保存数据6.数据展示 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫…

探索数据结构

什么是数据结构 数据结构是由&#xff1a;“数据”与“结构”两部分组成 数据与结构 数据&#xff1a;如我们所看见的广告、图片、视频等&#xff0c;常见的数值&#xff0c;教务系统里的&#xff08;姓名、性别、学号、学历等等&#xff09;&#xff1b; 结构&#xff1a;当…

Pandas进阶

文章目录 第1关&#xff1a;Pandas 分组聚合第2关&#xff1a;Pandas 创建透视表和交叉表 第1关&#xff1a;Pandas 分组聚合 编程要求 使用 Pandas 中的 read_csv() 函数读取 step1/drinks.csv 中的数据&#xff0c;数据的列名如下表所示&#xff0c;请根据 continent 分组并…

VMware 虚拟机自定义规范 - 更优雅的虚拟机开局

介绍 虚拟机自定义规范可以在你克隆虚拟机的时候在vCenter 的Web界面设定虚拟机的主机名、单/多网卡IP的IP和网关、DNS服务器、唯一标识符重置&#xff08;SID等&#xff09;、硬盘分区自动扩容、设定密码、密钥、时区等信息。 让管理员不需要进入虚拟机系统内部进行配置&…

10000字讲解IoC 思想以及五大注解

文章目录 IoC 思想通过案例讲解 IoC1.传统的开发方式 SpringIoC 和 DI五大注解ControllerServiceComponentRepositoryConfiguration 为什么要有这么多的类注解类注解之间的关系方法注解 Bean重命名 bean扫描路径 IoC 思想 什么是 Spring 呢&#xff1f; 我们经常听到的都是说…

Android 13 aosp 默认关闭SELinux

通过adb修改 adb root adb shell setenforce 0 // 开SELinux&#xff0c;设置成模式permissive adb shell setenforce 1 // 关SELinux&#xff0c;设置成模式enforce adb shell getenforce // 获取当前SELinux状态源码修改 Android_source/system/core/init/selinu…

JS-导入导出

export和export default是ES6中导出模块中变量的语法 导入导出变量 //导出方法&#xff08;js文件中&#xff09; export const 变量名值//导入方法 对应导入的变量&#xff0c;一定要加花括号 import {变量名} from js文件路径 导入导出函数 //导出方法&#xff08;js文件中…

2024.1IDEA 到2026年

链接&#xff1a;https://pan.baidu.com/s/1hjJEV5A5k1Z9JbPyBXywSw?pwd9g4i 提取码&#xff1a;9g4i解压之后,按照 操作说明.txt 操作; IntelliJ IDEA 2024.1 (Ultimate Edition) Build #IU-241.14494.240, built on March 28, 2024 Licensed to gurgles tumbles You have…

福汇美股开户教程

福汇作为全球知名的外汇交易平台&#xff0c;也提供美股交易服务。在福汇交易美股&#xff0c;首先需要开立一个福汇账户。本教程将详细介绍福汇美股开户流程。 第一步&#xff1a;访问福汇官网并填写开户表格 访问福汇美股入口点击页面顶部的“开户”按钮。选择您的国籍&…

JetsonNano —— Windows下对Nano板卡烧录刷机(官方教程)

介绍 NVIDIA Jetson Nano™ 开发者套件是一款面向创客、学习者和开发人员的小型 AI 计算机。按照这个简短的指南&#xff0c;你就可以开始构建实用的 AI 应用程序、酷炫的 AI 机器人等了。 烧录刷机 1、下载 Jetson Nano开发者套件SD卡映像&#xff0c;并记下它在计算机上的保存…

初探MFC程序混合使用QT

一、背景 随着操作系统国产化替代的趋势越发明显&#xff0c;软件支持国际化、跨平台&#xff0c;已然是必须做的一件事情。原有的软件UI层用的是MFC&#xff0c;将其换成QT&#xff0c;想必是一种较好的方案。对于大型软件&#xff0c;特别是已发布&#xff0c;但还处于不断迭…

vue 开发环境的搭建

一、整个流程&#xff1a; 安装nodejs >> 安装vue >> 安装vue-cli >> 初始化 webpack(生成代码) >> 安装依赖 >> 运行vue程序 二、详细安装流程&#xff1a; 1.安装nodejs 下载&#xff1a;https://nodejs.org/dist/v12.18.3/node-v12.18.3-x…

《米小圈上学记》|快乐读书,从身边的人身边的事开始!

时间&#xff0c;抓住了就是黄金&#xff0c;虚度了就是流水;书&#xff0c;看了就是学问&#xff0c;没看就是废纸:抱负&#xff0c;努力了才叫幻想&#xff0c;放弃了那只是妄想。读书&#xff0c;不一定能转变命运&#xff0c;但肯定能让我们安静&#xff0c;安静本身就是一…

【触摸案例-手势解锁案例-连线到按钮 Objective-C语言】

一、接下来,我们接着来说这个,连线的问题啊, 1.连线的问题啊,也就是说,我现在点击一个按钮, 在移动到下一个按钮的时候,在两个按钮中间,在两个按钮都亮起来的时候呢,我们肯定是让它去画一条线的,那么, 1)首先,如果我现在从第一个按钮,连到第二个按钮,那么,这条…

二叉树的实现(详解,数据结构)

目录 一&#xff0c;二叉树需要实现的功能 二&#xff0c;下面是各功能详解 0.思想&#xff1a; 1.创建二叉树结点&#xff1a; 2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 3.二叉树销毁&#xff1a; 4.前序遍历&#xff1a; 5.中序遍历&#xff1a;…
最新文章