java并发编程 ConcurrentLinkedQueue详解
文章目录
- 1 ConcurrentLinkedQueue是什么
- 2 核心属性详解
- 3 核心方法详解
- 3.1 add(E e)
- 3.2 offer(E e)
- 3.3 poll()
- 3.4 size()
- 3.5 并发情况分析
- 4 总结
1 ConcurrentLinkedQueue是什么
ConcurrentLinkedQueue是一个无界的并发队列,和LinkedBlockingQueue相比,它是通过完全的cas实现的,是非阻塞的。LinkedBlockingQueue是通过ReentrantLock实现的,提供了一些阻塞方法,如take() put()。
2 核心属性详解
//链表的头和尾节点private transient volatile Node<E> head;private transient volatile Node<E> tail;//Node的数据结构private static class Node<E> {//保存的元素volatile E item;//单向链表的当前Node的next节点volatile Node<E> next;Node(E item) {UNSAFE.putObject(this, itemOffset, item);}//cas设置当前item值boolean casItem(E cmp, E val) {return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);}//设置next节点的值void lazySetNext(Node<E> val) {UNSAFE.putOrderedObject(this, nextOffset, val);}//cas设置next节点的值boolean casNext(Node<E> cmp, Node<E> val) {return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);}//...}
3 核心方法详解
3.1 add(E e)
调用offer方法。在
public boolean add(E e) {return offer(e);
}
3.2 offer(E e)
首先在看这个方法之前,先了解一个掌握逻辑的方法。因为下面代码是无锁自旋(cas)代码,所以有很多触发条件,如果直接看是很难懂,
所以这里的小技巧是先不管多线程,去看逻辑。如下面的for循环,你先按照单线程调用了3~4次看看数据变化。先掌握它正常逻辑下的数
据结构的变化。因为是单向链表,看看节点之间是怎么变化的。
看下面流程再回头看这段代码。
public boolean offer(E e) {checkNotNull(e);final Node<E> newNode = new Node<E>(e);Node<E> t = tail;Node<E> p = t;for (;;) {Node<E> q = p.next;if (q == null) {// 追加节点 原子性操作,会有失败的情况if (p.casNext(null, newNode)) {// 跃过第一次设置tailif (p != t) // hop two nodes at a time//设置尾节点casTail(t, newNode); // Failure is OK.return true;}// Lost CAS race to another thread; re-read next}//poll情况,即存和取同时发生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.p = (t != (t = tail)) ? t : head;else// 第二次设置的时候q!=null的情况重新设置p节点往后移// Check for tail updates after two hops.p = (p != t && t != (t = tail)) ? t : q;}}
1.如果当前链表中无元素,此时根据构造器可知 head = tail = new Node<>(null); 此时添加一个元素。如图所示
此时 p.next == null 成立,所以会进入 casNext语句。此时成功了 p == t 是true, 所以返回true结束,此时数据结构变成下图
此时我再添加一个元素,p.next != null了,p == q也不成立, 所以走到最后一个else:p = (p != t && t != (t = tail)) ? t : q;
这段逻辑相当于 t =tail; 因为p == t 所以 p 变成了q。再次循环。
此时p就是NODE1了 q 是null了 走p.casNext设置NODE2 称为NODE1的next节点。注意!! 此时tail.next还是NODE1。如下图
此时再添加一个元素呢
此时流程中会命中p != t 重新设置tail, 此时node3就是tail
3.3 poll()
首先现在的数据结构是这样。
- 执行刚开始的时候 p 指向的是head 此时p的item == null。执行到 else p = q; 注意因为执行到else if ((q = p.next) 此时q = p.next,即p的下标到了head.next。此时在判断item是否是null 此时不是null了,去除n1 然后cas设置为null 此时p != h 因为往后移了一下,又因为 node1.next !=null 所以更新head为未node2。
此时如下图
- 如果再次poll
此时 p指向的是node2, 此时p.item != null 所以直接cas 设置成null 此时p == h成立 直接return,此时如下图
其实head是没动的。下次呢 此时item是空,那么又会向第一步一样。至此正常流程已经分析完
public E poll() {restartFromHead:for (;;) {Node<E> h = head;Node<E> p = h;Node<E> q = null;for (;;) {E item = p.item;if (item != null && p.casItem(item, null)) {// Successful CAS is the linearization point// for item to be removed from this queue.if (p != h) // hop two nodes at a timeupdateHead(h, ((q = p.next) != null) ? q : p);return item;}else if ((q = p.next) == null) {updateHead(h, p);return null;}else if (p == q)continue restartFromHead;elsep = q;}}}
3.4 size()
注意,因为他没有维护count字段,所以他计算数量是遍历计算的。不维护是因为上面是通过cas方式+循环保证原子性的,如果在加一个count字段,那失败重试的概率将大大增加
int count = 0;
for (Node<E> p = first(); p != null; p = succ(p))if (p.item != null)// Collection.size() spec says to max outif (++count == Integer.MAX_VALUE)break;
return count;
3.5 并发情况分析
上面已经分析了核心的入队列和出队列的两个方法,他不是实时更新head和tail节点,而是通过一次循环之后更新head和tail节点.
此时并发情况下,cas保证了原子性的设置。
-
offer方法
**p.casNext(null, newNode)**保证了原子性的追加链表元素。成功了设置tail 此时第一步成功不代表第二步(casTail(t, newNode))一定成功,因为此时可能别的线程已经改了tail。失败了怎么办呢? 失败了其实就是其他线程在offer的时候多循环几次,但是总有一个线程可以把第二步成功,也就是tail最后会回到尾部的。
p == q的情况,即p = p.next 出现这种情况就是此时p已经被移除 -
poll方法
(q = p.next) == null的情况是p从链表中删除,此时重新循环链表
4 总结
相对于LinkedBlockingQueue, 它实现了无锁化的方式。因为cas+for这种方式的逻辑很难梳理。所以大致了解思路吧。
相关文章:

java并发编程 ConcurrentLinkedQueue详解
文章目录 1 ConcurrentLinkedQueue是什么2 核心属性详解3 核心方法详解3.1 add(E e)3.2 offer(E e)3.3 poll()3.4 size()3.5 并发情况分析 4 总结 1 ConcurrentLinkedQueue是什么 ConcurrentLinkedQueue是一个无界的并发队列,和LinkedBlockingQueue相比,…...

msvcp110.dll是什么意思与msvcp110.dll丢失的解决方法
电脑突然提示msvcp110.dll丢失,无法执行此代码。导致软件无法打开运行,这个怎么办呢?我在网上找了一天的资料,终于把这个问题彻底处理好,也弄清楚了msvcp110.dll丢失的原因及msvcp110.dll丢失修复方法?现在…...

八)Stable Diffussion使用教程:MultiDiffusion
multidiffusion,它可以实现图片从 512 像素到 2K、4K 甚至 6K 画质的飞跃。 插件安装步骤: 1)选择扩展 2)选择可用,点击加载按钮 3)找到multidiffusion,点击右侧安装按钮 安装插件后可以在文生图和图生图的出图参数中看到多了两个区域,其实这个插件是由两部分组成的,…...
java通过钉钉机器人发消息
钉钉自定义机器人使用 加签的配置 发送消息 注意:内部群才可以创建自定义机器人 钉钉网址-自定义机器人创建 1、获得的钉钉配置信息workhook和secret //url路径private String URL "https://oapi.dingtalk.com/robot/send?access_token08ebaa04f98f7faacb…...
Git工具本地管理总结
一、本地仓库创建 https://blog.csdn.net/heshuangzong/article/details/125882372 https://blog.csdn.net/l7077/article/details/130270914 在本地创建/home/test目录,作为本地仓库目录。 $ mkdir /home/test $ cd /home/test 初始化本地的git 仓库。 $ git init Initial…...
单片机C语言实例:13、看门狗
一、看门狗溢出测试 程序实例1: #include<reg52.h>sfr WDTRST 0xA6; sbit key P3^1; /*------------------------------------------------喂狗 ------------------------------------------------*/ void Rst_Watchdog( void ) {WDTRST 0x1E…...

时序分解 | MATLAB实现基于SSA奇异谱分析的信号分解分量可视化
时序分解 | MATLAB实现基于LMD局部均值分解的信号分解分量可视化 目录 时序分解 | MATLAB实现基于LMD局部均值分解的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 奇异谱分解奇异谱分析SSA 可直接替换txt数据运行 Matlab 1.包含3D分解效果图 频谱图等…...
mysql报错:Duplicate entry ‘...‘ for key ‘field‘
错误信息 "Duplicate entry ... for key field" 表示在数据库表中,你正在尝试插入一条数据的number字段的值已经存在。这通常是由于你设置了field字段为唯一键(UNIQUE KEY),而你又尝试插入一个已存在的值。 解决这个问…...
什么是回流跟重绘?从中怎么优化网页性能?
目录 一、什么是回流? 二、什么是重绘? 三、如何触发回流和重绘?会带来什么问题? 四、如何减少回流和重绘的影响? 在前端开发中,回流(reflow)和重绘(repaint…...
Redis事务机制
Redis 是一款开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。在日常的使用中,我们经常会遇到需要一次执行多个命令,并且这些命令要么全部成功,要么全部失败的场景。这就需要用到 Redis 的事务机制。 Redi…...

[EROOR] SpringMVC之500 回调函数报错
首先,检查一下idea里面的报错的原因,我的是jdk的版本的问题。所以更换一下就可以了。...

[Linux]文件系统
[Linux]文件系统 文件系统是操作系统的一部分,负责组织、存储和管理存储在外部设备上的文件和目录,也就是操作系统管理外设中的文件的策略。本文讲解的是Ext2文件系统。Linux操作系统使用的就是Ext系列的文件系统。 文章目录 [Linux]文件系统了解磁盘结构…...
常见面试题记录
记录下java的常见面试题 文章目录 记录如下 记录如下 记录如下 hashmap原理lock原理synchronized锁优化过程线程状态以及创建方式线程池(执行过程,参数,淘汰策略)jvm(gc优化和OOM)volatile(可见…...

Android 系统源码目录frameworks/base/packages和packages/apps下的APP区别
概要 在 Android Open Source Project (AOSP) 源代码中,frameworks/base/packages 和 packages/apps 目录都包含 Android 系统中的应用程序,但它们在性质和用途上有一些区别: 1,frameworks/base/packages frameworks/base 目录…...

2023年数维杯数学建模A题河流-地下水系统水体污染研求解全过程文档及程序
2023年数维杯数学建模 A题 河流-地下水系统水体污染研 原题再现: 河流对地下水有着直接地影响,当河流补给地下水时,河流一旦被污染,容易导致地下水以及紧依河流分布的傍河水源地将受到不同程度的污染,这将严重影响工…...

Java测试(10)--- selenium
1.定位一组元素 (1)如何打开本地的HTML页面 拼成一个URL :file: /// 文件的绝对路径 import os os.path.abspath(文件的绝对路径) (2)先定位出同一类元素(tag name,name&…...

【文末送书】Matlab科学计算
欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…...
ElementUI浅尝辄止30:PageHeader 页头
如果页面的路径比较简单,推荐使用页头组件而非面包屑组件。 1.如何使用? <el-page-header back"goBack" content"详情页面"> </el-page-header><script>export default {methods: {goBack() {console.log(go bac…...

[Qt]基础数据类型和信号槽
文章目录 1. Qt基本结构1.1 Qt本有项目1.1.1 项目文件(.pro)1.1.2 main.cpp1.1.3 mainwindow.ui1.1.4 mainwindow.h1.1.5 mainwindow.cpp 1.2 Qt中的窗口类1.2.1基础窗口类1.2.2 窗口的显示 1.3 内存回收 2. Qt中的基础数据类型2.1 基础类型2.2 log输出2…...

UIStackView入门使用两个问题
项目中横向一排元素,竖向一排元素,可以使用UIStackView。UIStackView的原理不做介绍,这里主要讲两个初次使用容易出现的两个问题。 首先创建一个stackview -(UIStackView*)titleStackView{if(_titleStackView nil){_titleStackView [UISta…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...