java并发编程 ConcurrentLinkedQueue详解

news2025/1/23 2:09:52

文章目录

  • 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)) {
                    // 跃过第一次设置tail
                    if (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()

在这里插入图片描述
首先现在的数据结构是这样。

  1. 执行刚开始的时候 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。
    此时如下图
    在这里插入图片描述
  2. 如果再次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 time
                        updateHead(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;
                else
                    p = 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 out
        if (++count == Integer.MAX_VALUE)
            break;
return count;

3.5 并发情况分析

上面已经分析了核心的入队列和出队列的两个方法,他不是实时更新head和tail节点,而是通过一次循环之后更新head和tail节点.
此时并发情况下,cas保证了原子性的设置。

  1. offer方法
    **p.casNext(null, newNode)**保证了原子性的追加链表元素。成功了设置tail 此时第一步成功不代表第二步(casTail(t, newNode))一定成功,因为此时可能别的线程已经改了tail。失败了怎么办呢? 失败了其实就是其他线程在offer的时候多循环几次,但是总有一个线程可以把第二步成功,也就是tail最后会回到尾部的。
    p == q的情况,即p = p.next 出现这种情况就是此时p已经被移除

  2. poll方法
    (q = p.next) == null的情况是p从链表中删除,此时重新循环链表

4 总结

相对于LinkedBlockingQueue, 它实现了无锁化的方式。因为cas+for这种方式的逻辑很难梳理。所以大致了解思路吧。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【新版】系统架构设计师 - 软件架构设计<轻量级架构>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 软件架构设计&#xff1c;轻量级架构&#xff1e;考点摘要轻量级架构表示层业务逻辑层持久层数据库 SSH与SSMHibernate与Mybatis 架构 - 软件架构设计&#xff1c;轻量级架构&#xff1e; 考点摘…

九)Stable Diffussion使用教程:ControlNet

在 ControlNet 出现之前&#xff0c;基于扩散模型的 AI 绘画是极难控制的&#xff0c;因为扩散的过程充满了随机性。 如果只是纯粹自娱自乐&#xff0c;这种随机性并不会带来多大困扰&#xff1b; 但在产业化上应用就难以普及了&#xff0c;因为随机性直接导致的就是缺乏稳定…

【C++漂流记】一文搞懂类与对象中的对象特征

在C中&#xff0c;类与对象是面向对象编程的基本概念。类是一种抽象的数据类型&#xff0c;用于描述对象的属性和行为。而对象则是类的实例&#xff0c;具体化了类的属性和行为。本文将介绍C中类与对象的对象特征&#xff0c;并重点讨论了对象的引用。 文章目录 一、构造函数和…

【云原生进阶之PaaS中间件】第二章Zookeeper-1-综述

1 Zookeeper基础 1.1 简介 ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;它包含一个简单的原语集&#xff0c;分布式应用程序可以基于它实现同步服务&#xff0c;配置维护和命名服务等。 Zookeeper是hadoop的一个子项目&#xff0c;其发…

职场工作与生活

序言&#xff1a; 和很多在CSDN的博主一样&#xff0c;大家在工作之后就很少或者是不再回到CSDN&#xff0c;确实自己也一年多没上了。因为可能当初大家在这就是为了记录和分享当初自己学习技术的东西。而大家走出象牙塔开始工作后&#xff0c;发生了很大的转变。在国内…

2核2G3M带宽服务器腾讯云和阿里云价格、性能对比

2核2G云服务器可以选择阿里云服务器或腾讯云服务器&#xff0c;腾讯云轻量2核2G3M带宽服务器95元一年&#xff0c;阿里云轻量2核2G3M带宽优惠价108元一年&#xff0c;不只是轻量应用服务器&#xff0c;阿里云还可以选择ECS云服务器u1&#xff0c;腾讯云也可以选择CVM标准型S5云…

堆相关例子-最大线段重合问题

问题描述 给定很多线段&#xff0c;每个线段都有两个数[start, end]&#xff0c; 表示线段开始位置和结束位置&#xff0c;左右都是闭区间 规定&#xff1a; 1&#xff09;线段的开始和结束位置一定都是整数值 2&#xff09;线段重合区域的长度必须>1 返回线段最多重合…

Alibaba(获得店铺的所有商品) API 接口

为了进行电商平台 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个alibaba应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载alibaba API的SDK并掌握基本的API基础知识和调用 4&#xff09;利…

appium+jenkins实例构建

自动化测试平台 Jenkins简介 是一个开源软件项目&#xff0c;是基于java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 前面我们已经开完测试脚本&#xff0c;也使用bat 批处…

Linux TCP和UDP协议

目录 TCP协议TCP协议的面向连接1.三次握手2.四次挥手 TCP协议的可靠性1.TCP状态转移——TIME_WAIT 状态TIME_WAIT 状态存在的意义&#xff1a;&#xff08;1&#xff09;可靠的终止TCP连接。&#xff08;2&#xff09;让迟来的TCP报文有足够的时间被识别并被丢弃。 2.应答确认、…

linux 堆探索

堆的虚拟地址是连续的&#xff0c;是brk来分配&#xff0c;brk是一个指针指向堆顶的指针&#xff0c;并且是可以复用的&#xff0c;但是只有在堆顶空闲128k时&#xff0c;才收缩&#xff0c;也就是说&#xff0c;为了减少page_fault&#xff0c;可重用&#xff0c;开销小的特点…

消息队列--入门篇

消息队列–入门篇 目录 消息队列--入门篇如何学习一门新技术&#xff1f;消息队列的组件大体介绍一下producerproducer groupnameSrvbrokerbroker clusterconsumer和consumer groupTopicTag 总结 如何学习一门新技术&#xff1f; 学习任何知识需要有一个整体的认识&#xff0c…

【数据分享】2006-2021年我国省份级别的园林绿化相关指标(多项指标)

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况&#xff0c;在之前的文章中&#xff0c;我们分享过基于2006-2021年《中国城市建设统计年鉴》整理的2006—2021年我国省份级别的市政设施水平相关指标、2006-2021年我国省份级别的各类建设用地面积数…

【Linux】进程概念I --操作系统概念与冯诺依曼体系结构

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 1. 冯诺依曼体系结构为什么这样设计? 2. 操作系统概念为什么我们需要操作系统呢?操作系统怎么进行管理? 计算机是由两部分组…

使用Xshell远程访问工具连接到Linux

首先需要查看Linux地址&#xff0c;在Linux主界面中右键选择“Open in Terminal” 输入“ifconfig”指令查看IP地址 打开Xshell&#xff0c;输入相关信息&#xff0c;建立连接 点击连接&#xff0c;按照提示输入用户名 root和你自己安装centos7时设置的密码&#xff0c;用…

css实现圆角三角形,圆角三角形的实现

今天给大家带来一个如何实现圆角三角形的方案&#xff0c;这个方案虽然可以实现&#xff0c;但是也是借助拼凑等方式来实现的&#xff0c;假如想一个div来实现圆角三角形&#xff0c;还是比较困难的。之前文章讲了如何实现对话框&#xff0c;里面介绍了三角形的实现方式。今天讲…

使用navicat for mongodb连接mongodb

使用navicat for mongodb连接mongodb 安装navicat for mongodb连接mongodb 安装navicat for mongodb 上文mongodb7.0安装全过程详解我们说过&#xff0c;在安装的时候并没有勾选install mongodb compass 我们使用navicat去进行可视化的数据库管理 navicat for mongodb下载地址…

秒杀“超卖”问题

概述&#xff1a;限时秒杀活动在我们的日常生活中有很多&#xff0c;尤其在“双11”&#xff0c;“618”这类购物节活动中用户的并发数更是海量剧增&#xff0c;那么系统为了防止“超卖”秒杀商品&#xff0c;怎么做才能不影响性能的同时防止超卖。 为了解决“超卖”问题有两种…

mysql或ps提示 vcruntime140_1.dll丢失如何修复,5种方法办法你搞定

今天我在运行一款新安装的软件时&#xff0c;突然遇到了一个让我十分困扰的问题——系统提示vcruntime140_1.dll文件丢失。这个问题导致我无法正常使用这个软件&#xff0c;我也不知道怎么回事&#xff0c;所以我在网上找了一天终于让我找到解决这个问题的方法了&#xff0c;今…

绩效只是绩效

绩效只是绩效 一、背景二、绩效和管理三、绩效和薪资四、QA环节五、总结与反思 一、背景 目前大部分主体的绩效考核方向不明确&#xff0c;有的只是为了考核而考核&#xff0c;那么绩效是什么&#xff1f; 打A&#xff08;超出预期&#xff09;和打D&#xff08;低于预期&…