ConcurrentLinkedQueue源码解析

ConcurrentLinkedQueue源码解析

ConcurrentLinkedQueue是一个以链表实现的线程安全的队列

初始化

Queue<String> queue = new ConcurrentLinkedQueue();

初始化具体实现

private transient volatile Node<E> head;// 头部节点()
private transient volatile Node<E> tail;// 尾部节点()

public ConcurrentLinkedQueue() {
	//生成一个节点,并用head和tail都指向它,因为此时的Queue中并没有添加任何东西,head和tail指向同一个节点
    head = tail = new Node<E>(null);
}

ConcurrentLinkedQueue是由node类实现的,大略看看这个类的构造

private static class Node<E> {
        volatile E item;
        volatile Node<E> next;//这里可以说明ConcurrentLinkedQueue是以链表实现的
    
        Node(E item) {
            UNSAFE.putObject(this, itemOffset, item);
        }
    ...
}

添加元素

// 循环向队列中添加字母表中字母
for (int i = 0; i < 24; i++) {
    queue.add( String.valueOf((char) (97 + i))  );
}

add 方法

public boolean add(E e) {
    return offer(e);
}

offer 方法

🔔 在阅读offer源码前,希望你能带上C++的指针思想

public boolean offer(E e) {
    // 判断e是否是一个空指针,或空对象,如果是空,直接抛出异常
    checkNotNull(e);
    // 生成新节点
    final Node<E> newNode = new Node<E>(e);
    // 循环,t指向tail(尾部节点),p指向t,也就是p指向tail,这是一个自旋,因为多个线程操作链表时,举例A、B线程,他们
    // 还没有开始添加元素时p指向最后一个节点i,如果A线程已经添加了一个新节点k,此时的p应该指向k,而B线程的p还指向原节点i,
    // 这显然不行!for循环内用了CAS原理保证了同一时刻只有一个线程能操作成功,但是不可能A线程添加成功了,B线程添加就失败吧,
    // 这也不行,所以用自旋,如果CAS失败,就更新p的指向,直至B线程成功添加为止
    for (Node<E> t = tail, p = t;;) {
        // 用q记录p的下一个节点
        Node<E> q = p.next;
        // 判断p.next是否是null,是null证明p已经指向链表的最后一个节点
        if (q == null) {
            // if(q==null)成立,说明p是最后一个节点了
            // 调用casNext方法比较p.next指向是否为null(CAS原理),如果是则更新p.next的值,把p.next指向newNode
            // CAS作用:多线程操作下,可能A线程进行到此处,而B线程已经完成了对链表的add操作,等到A继续运行时,p一定不是
            // 最后一个节点,利用cas乐观锁的机制,验证p.next是否还是null,如果是null证明其他线程并没有对p操作过,此时
            // 可以完成将p.next指向newNode,如果不是null,那么if判断为false,跳出if
            if (p.casNext(null, newNode)) {
                // 此时CAS成功了,p的next属性已经指向newNode了,注意:p.casNext(null, newNode)仅修改了p的next的指向,
                // 并没有修改p的指向,也就是说p和t指向的还是原来的tail;
                
                //一次跳两个节点更新tail指向
                if (p != t) // hop two nodes at a time
                    casTail(t, newNode);  // Failure is OK.
                return true;
            }
        }
        else if (p == q)
            p = (t != (t = tail)) ? t : head;
        else
            p = (p != t && t != (t = tail)) ? t : q;
    }
}

ConcurrentLinkedQueue 每隔一次,调整一次tail的指向,有助于减少在高度并发环境下的循环次数,提高效率,这块理解起来有难度!

posted @   勤匠  阅读(8)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示