ConcurrentLinkedQueue

简介: ConcurrentLinkedQueue

ConcurrentLinkedQueue

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规
则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部;当我们获取一个元
素时,它会返回队列头部的元素。

ConcurrentLinkedQueue由head节点和tail节点组成,每个节点(Node)由节点元素(item)和
指向下一个节点(next)的引用组成,节点与节点之间就是通过这个next关联起来,从而组成一
张链表结构的队列。默认情况下head节点存储的元素为空,tail节点等于head节点。

入队列

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;
        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".
                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
        }
        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
            // Check for tail updates after two hops.
            p = (p != t && t != (t = tail)) ? t : q;
    }
}

整个入队过程主要做两件事情:第一是定位出尾节点;第二是使用CAS算法将入队节点设置成尾节点的next节点,如不成功则重试。

出队列

并不是每次出队时都更新head节点,当head节点里有元素时,直接弹出head节点里的元素,而不会更新head节点。只有当head节点里没有元素时,出队操作才会更新head节点。这种做法也是通过hops变量来减少使用CAS更新head节点的消耗,从而提高出队效率。

public E poll() {
    restartFromHead:
    for (;;) {
        for (Node<E> h = head, p = h, q;;) {
            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;
        }
    }
}

首先获取头节点的元素,然后判断头节点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作将该节点的元素取走,如果不为空,则使用CAS的方式将头节点的引用设置成null,如果CAS成功,则直接返回头节点的元素,如果不成功,表示另外一个线程已经进行了一次出队操作更新了head节点,导致元素发生了变化,需要重新获取头节点。

相关文章
|
存储 安全 Java
set集合的特性与详细介绍
set集合的特性与详细介绍
228 1
|
机器学习/深度学习 数据可视化 TensorFlow
使用Python实现深度学习模型:智能旅游路线规划
使用Python实现深度学习模型:智能旅游路线规划
256 2
|
存储 编解码 Android开发
NV21、NV12、YV12、RGB、YUV、RGBA、RGBX8888等图像色彩编码格式区别
NV21、NV12、YV12、RGB、YUV、RGBA、RGBX8888都是常见的图像颜色编码格式,它们之间的主要区别在于色彩空间和数据排列方式。
328 0
|
数据库
系统分析与设计问题之在软件开发中,为什么需要考虑变化
系统分析与设计问题之在软件开发中,为什么需要考虑变化
|
JSON 数据格式
非常实用的5种json数组去重方法,函数实现思路竟是chatgpt帮我写的!
你敢信这5种json数组去重方法的实现思路竟然是chatgpt写的,chatgpt对函数的理解也太准确了吧!
482 0
|
XML 数据格式 C++
protobuf C++ 使用示例
1、在.proto文件中定义消息格式 2、使用protobuf编译器 3、使用c++ api来读写消息   0、为何使用protobuf?   1、原始内存数据结构,可以以二进制方式sent/saved.这种方式需要相同的内存布局和字节序。
8564 0
|
网络安全 Windows
ssh和scp连接window服务器
ssh和scp连接window服务器
576 0
|
前端开发
初学鸿蒙OS之JS-UI框架中基础组件的使用
初学鸿蒙OS之JS-UI框架中基础组件的使用
145 0
|
关系型数据库 MySQL
MySQL: INSERT INTO SELECT 语句实现数据快速复制
MySQL: INSERT INTO SELECT 语句实现数据快速复制
399 0
|
Java 容器 Spring
Spring Boot---使用@ServerEndpoint创建WebSocket
Spring Boot---使用@ServerEndpoint创建WebSocket
Spring Boot---使用@ServerEndpoint创建WebSocket

热门文章

最新文章