ConcurrentLinkedQueue的原理以及CAS机制在其中的应用_concurrentlinkedqueue cas-程序员宅基地

技术标签: JAVA多线程与高并发  Node  CAS机制  UNSAFE  

ConcurrentLinkedQueue是一个线程安全队列,区别于阻塞算法的锁机制,它使用了基于CAS机制的非阻塞算法。
基本概念介绍:
队列:通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。
接口Queue

接口quque中的方法可以分为三类
添加队列尾元素  
boolean add(E e)与boolean offer(E e)
add结果只能为返回true(成功插入)或抛出异常(不成功),根据异常类型(IllegalStateException)才能看出是否由于对列容量限制
offer结果返回true(成功插入)或返回false(容量限制)或抛出异常

获取队列头但不移除  
E element() 与 E peek()
区别在于当队列为空时 elemnet()方法抛出异常
peek方法返回null

获取对列头并移除
remove() 与 poll()
区别在于队列为空时 remove()方法抛出异常
poll方法返回null

之前文章说明过CAS机制的依据(硬件层面的是实现):http://blog.csdn.net/do_finsh/article/details/78773573
以及大体java.util.concurrent包下的类实现的共同点:

  1. .声明共享变量为volatile;(没有原子性,有可见性)
  2. 使用CAS的原子条件更新来实现线程之间的同步;
  3. 配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。

现在具体说明一下ConcurrentLinkedQueue中的应用

首先出现了一个私有静态类Node
这个Node类中出现了UNSAFE类,节点类Node< E> 通过UNSAFE类的方法,进行CAS操作。大致了解一下
UNSAFE类的方法

    // 获取f在堆内存的偏移地址
    public native long objectFieldOffset(Field f);

    // 获取静态f在堆内存的偏移地址
    public native long staticFieldOffset(Field f);

    // 原子的更改offset地址的变量。如果变量的值为expected,并成功替换为x 返回true  CAS操作
    public final native boolean compareAndSwapObject(Object o, long offset, Object expected,Object x);

    // 将offset地址的值替换为x,并且通知其他线程。因为有Volatile,与putObject类似
    public native void putObjectVolatile(Object o, long offset, Object x);

    // 获取地址为offset 的值
    public native Object getObjectVolatile(Object o, long offset)

   /**
     * Version of {
   @link #putObjectVolatile(Object, long, Object)}
     * that does not guarantee immediate visibility of the store to
     * other threads. This method is generally only useful if the
     * underlying field is a Java volatile (or if an array cell, one
     * that is otherwise only accessed using volatile accesses).
     */
public native void putOrderedObject(Object o, long offset, Object x);

节点类Node

private static class Node<E> {
    
        volatile E item; // volatile声明
        volatile Node<E> next; // volatile声明
        // 构造方法  在地址itemOffset处, 值替换为item
        Node(E item) {
            UNSAFE.putObject(this, itemOffset, item);
        }
        // CAS操作:比较并交换,原子的更改itemOffset地址的变量。如果变量的值为cmp,并成功替换为val 返回true  
        boolean casItem(E cmp, E val) {
            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }
       // 将nextOffset地址的值替换为x
        void lazySetNext(Node<E> val) {
            UNSAFE.putOrderedObject(this, nextOffset, val);
        }
        // CAS操作:比较并交换,原子的更改nextOffset地址的变量。如果变量的值为cmp,并成功替换为val 返回true
        boolean casNext(Node<E> cmp, Node<E> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        // Unsafe mechanics

        private static final sun.misc.Unsafe UNSAFE;
        private static final long itemOffset;
        private static final long nextOffset;
        /**
          * 静态代码块,获取itemOffset和nextOffset
          */

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class k = Node.class;
                itemOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("item"));
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

ConcurrentLinkedQueue的常用方法offer(E e);

public boolean offer(E e) {
        checkNotNull(e);
        //创建一个入队节点
        final Node<E> newNode = new Node<E>(e);
        // 死循环,直到return ture 
        // 创建一个指向tail节点的引用t
        // p用来表示尾结点
        for (Node<E> t = tail, p = t;;) { 
            // 获取p的下一节点
            Node<E> q = p.next;
            // 如果q为空,说明p是尾节点
            if (q == null) {
                // p是尾节点,把e放入p节点之后,CAS操作(原子性操作),成功,跳出循环
                if (p.casNext(null, newNode)) {
                    if (p != t) 
                    // 如果tail不是尾节点则将入队节点设置为tail。如果失败了,那么说明有其他线程已经把tail移动过
                        casTail(t, newNode); 
                    return true;
                }
            }
            // 如果p节点等于p的next节点,则说明p节点和q节点都为空,表示队列刚初始化,所以返回 
            else if (p == q)
                p = (t != (t = tail)) ? t : head;
            else
                //p有next节点,表示p的next节点是尾节点,则需要重新更新p后将它指向next节点
                //此步判断非原子性操作t != (t = tail)
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/do_finsh/article/details/78789964

智能推荐

redis6.2 使用 TLS 的部署(三种高可用模式)_redis 6.2版本启用tls认证-程序员宅基地

文章浏览阅读3.5k次。redis6.2 使用 TLS 的部署"三种高可用模式"安装redis6.2 并启用TLS加密安装创建TLS证书编写配置文件systemd管理测试连接redis 主从 配置 tls安装拷贝master 证书 到 slave编写配置文件systemd管理验证主从服务sentinel 哨兵服务创建目录、配置文件systemd管理查看sentinel 状态redis cluster 分片集群TLS安装规划目录及证书编写配置文件创建cluster 集群验证cluster 集群安装redis6.2 并启用TLS加密_redis 6.2版本启用tls认证

Sentinel-2波段合成_sentinel2 数据波段合成-程序员宅基地

文章浏览阅读378次。在上一篇博客中下载了Sentinel-2数据,他有13个波段的.jp2文件,下面选取需要使用的波段进行合成。导入了B2(蓝色)、B3(绿色)、B4(红色)、B8(近红外),直接存为TIFF会丢失坐标信息。最后将合成好的影像存为TIFF。得到下面这些.dat格式。_sentinel2 数据波段合成

【图像融合】基于错误纹理消除和显著特征检测的多模态脑图像融合神经科学前沿2023文献matlab复现-程序员宅基地

文章浏览阅读516次,点赞7次,收藏10次。多模态脑影像融合旨在将来自不同成像方式的脑影像数据进行融合,以获得更全面的信息。然而,由于不同成像方式的图像特性差异较大,直接融合会导致误差纹理和显著特征丢失等问题。本文提出了一种基于误差纹理消除和显著特征检测的多模态脑影像融合方法。该方法首先通过误差纹理消除算法去除融合图像中的误差纹理,然后通过显著特征检测算法提取融合图像中的显著特征,最后将显著特征融合到误差纹理消除后的图像中,得到最终的融合图像。实验结果表明,该方法可以有效消除误差纹理,保留显著特征,获得高质量的融合图像。引言。

python bar3d color_三维散点图colorbar matplotlib Python-程序员宅基地

文章浏览阅读1k次。我无法将颜色条添加到我的三维散点图中,它的颜色范围是根据bifurWidth的值在min和{}之间。我尝试过stackoverflow上显示的各种尝试,但都没有成功。任何帮助都将是非常感谢,因为我在这方面是一个重大的损失。在我最近的一次尝试是从下面的代码中散列出来的。在我的代码:from glob import globfrom pylab import *import numpy as npfr..._bar3d 好看的颜色

7.CSS-交集选择器、并集选择器、兄弟选择器_交集选择器和并集选择器使用方式-程序员宅基地

文章浏览阅读1.7k次。文章目录*交集选择器作用格式注意点并集选择器作用格式注意点相邻兄弟选择器作用格式注意点通用兄弟选择器作用格式注意点*交集选择器作用给所有选择器选中的标签中,相交的那部分设置属性。格式选择器1 选择器2{ 属性:值;}注意点1.选择器和选择器之间没有任何连接符号;2.选择器可以使用标签名称/id名称/class名称;3.仅了解。并集选择器作用给所有选择器选中的标签设置属性。格式选择器1,选择器2{属性:值;}注意点1.并集选择器必须使用,来连接;2.选择器可以使用_交集选择器和并集选择器使用方式

Windows CMD命令大全-程序员宅基地

文章浏览阅读79次。打开cmd快捷键 window键+RCMD命令锦集 1. gpedit.msc-----组策略  2. sndrec32-------录音机  3. Nslookup-------IP地址侦测器 ,是一个 监测网络中 DNS 服务器是否能正确实现域名解析的命令行工具。 它在 Windows NT/2000/XP 中均可使用 , 但在 Wi...

随便推点

计算机图形图像处理在教学中的应用,计算机图形图像处理案例教学法运用-程序员宅基地

文章浏览阅读534次。摘要:笔者根据计算机图形图像处理课程与中职学生学习的特点,分析了目前中职学校计算图形图像处理课程教学中存在的问题,针对如何提高中职学生对计算机图形图像处理课程的学习兴趣和解决实际问题的能力,提出了案例教学法在该课程中的具体实施办法,并对其实践进行了进一步的讨论。关键词:计算机图形图像处理;案例教学;中职当今世界电子商务发展迅速,计算机平面设计这门技术在很多领域都得到广泛应用。《Photoshop图..._图像分类在教育中的应用

python资源文件嵌入exe_pyinstaller将资源文件打包进exe中-程序员宅基地

文章浏览阅读921次。在网上看了很多博客,终于找到了符合自己智商可理解的打包资源文件方法,现引用如下https://www.cnblogs.com/darcymei/p/9397173.htmlhttps://blog.csdn.net/sinat_27382047/article/details/81304065"""终于把资源文件加载进去了,就是当exe文件移植后,它运行的时候会产生一个临时文件夹,把资源文件存储到..._pyinstaller如何将_internal添加进exe

H3C模拟器配置vlan-程序员宅基地

文章浏览阅读3.3k次,点赞4次,收藏7次。Valn 11组网需求• 交换机GE_2上的VLAN 5 和VLAN 10 为Primary VLAN,其上层端口GigabitEthernet1/0/1需要允许VLAN 5 和VLAN 10 的报文携带VLAN Tag 通过。• 交换机GE_2 的下行端口GigabitEthernet1/0/2 允许Secondary VLAN 2 通过,GigabitEthernet1/0/3 允许Sec..._新华3模拟器vlan配置

img撑满全屏的方法(img非背景图)_img 铺满-程序员宅基地

文章浏览阅读4.7w次,点赞8次,收藏15次。我有一个模板,想按常规做一个div里面放置一个img图片,并且让图片铺满容器,自适应容器大小。HTML结构代码如下(在这个盒模型上,我已经放置了一些不重要的样式)。div style="height:270px;width:400px;border:2px black solid;"> a href="http://www.paipk.com">img src="..." alt="拍_img 铺满

UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xb0 in position 5: invalid start byte_ebpf bcc unicodedecodeerror: 'utf-8' codec can't d-程序员宅基地

文章浏览阅读947次。UnicodeDecodeError: 'gbk' codec can't decode byte 0xfa in position 4669: illegal multibyte sequenceUnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 5: invalid start bytewith open('进线汇总20201211.csv',encoding='utf8') as f: t = f._ebpf bcc unicodedecodeerror: 'utf-8' codec can't decode byte 0xb0 in positio

使用条件序列GAN改进NMT_improving neural machine translation with conditio-程序员宅基地

文章浏览阅读1.3k次。使用条件序列GAN改进NMT原文《Improving Neural Machine Translation with Conditional Sequence Generative Adversarial Nets》课程作业,因为要导出pdf所以粘贴到CSDN了,34章是笔者翻译的部分。当一篇post吧,求别喷,有问题请留言我一定改,一定改。摘要本文提出了一种将GANs应用于NMT领域的方..._improving neural machine translation with conditional sequence generative ad