网创优客建站品牌官网
为成都网站建设公司企业提供高品质网站建设
热线:028-86922220
成都专业网站建设公司

定制建站费用3500元

符合中小企业对网站设计、功能常规化式的企业展示型网站建设

成都品牌网站建设

品牌网站建设费用6000元

本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...

成都商城网站建设

商城网站建设费用8000元

商城网站建设因基本功能的需求不同费用上面也有很大的差别...

成都微信网站建设

手机微信网站建站3000元

手机微信网站开发、微信官网、微信商城网站...

建站知识

当前位置:首页 > 建站知识

java集合之ConcurrentLinkedQueue的示例代码-创新互联

这篇文章给大家分享的是有关java集合之ConcurrentLinkedQueue的示例代码的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

成都创新互联公司主营平乡网站建设的网络公司,主营网站建设方案,手机APP定制开发,平乡h5微信小程序搭建,平乡网站营销推广欢迎平乡等地区企业咨询

问题

(1)ConcurrentLinkedQueue是阻塞队列吗?

(2)ConcurrentLinkedQueue如何保证并发安全?

(3)ConcurrentLinkedQueue能用于线程池吗?

简介

ConcurrentLinkedQueue只实现了Queue接口,并没有实现BlockingQueue接口,所以它不是阻塞队列,也不能用于线程池中,但是它是线程安全的,可用于多线程环境中。


那么,它的线程安全又是如何实现的呢?让我们一起来瞧一瞧。

源码分析

主要属性

// 链表头节点
private transient volatile Node head;
// 链表尾节点
private transient volatile Node tail;

就这两个主要属性,一个头节点,一个尾节点。

主要内部类

private static class Node {
    volatile E item;
    volatile Node next;
}

典型的单链表结构,非常纯粹。

主要构造方法

public ConcurrentLinkedQueue() {
    // 初始化头尾节点
    head = tail = new Node(null);
}

public ConcurrentLinkedQueue(Collection c) {
    Node h = null, t = null;
    // 遍历c,并把它元素全部添加到单链表中
    for (E e : c) {
        checkNotNull(e);
        Node newNode = new Node(e);
        if (h == null)
            h = t = newNode;
        else {
            t.lazySetNext(newNode);
            t = newNode;
        }
    }
    if (h == null)
        h = t = new Node(null);
    head = h;
    tail = t;
}

这两个构造方法也很简单,可以看到这是一个×××的单链表实现的队列。

入队

因为它不是阻塞队列,所以只有两个入队的方法,add(e)和offer(e)。

因为是×××队列,所以add(e)方法也不用抛出异常了。

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

public boolean offer(E e) {
    // 不能添加空元素
    checkNotNull(e);
    // 新节点
    final Node newNode = new Node(e);

    // 入队到链表尾
    for (Node t = tail, p = t;;) {
        Node q = p.next;
        // 如果没有next,说明到链表尾部了,就入队
        if (q == null) {
            // CAS更新p的next为新节点
            // 如果成功了,就返回true
            // 如果不成功就重新取next重新尝试
            if (p.casNext(null, newNode)) {
                // 如果p不等于t,说明有其它线程先一步更新tail
                // 也就不会走到q==null这个分支了
                // p取到的可能是t后面的值
                // 把tail原子更新为新节点
                if (p != t) // hop two nodes at a time
                    casTail(t, newNode);  // Failure is OK.
                // 返回入队成功
                return true;
            }
        }
        else if (p == q)
            // 如果p的next等于p,说明p已经被删除了(已经出队了)
            // 重新设置p的值
            p = (t != (t = tail)) ? t : head;
        else
            // t后面还有值,重新设置p的值
            p = (p != t && t != (t = tail)) ? t : q;
    }
}

入队整个流程还是比较清晰的,这里有个前提是出队时会把出队的那个节点的next设置为节点本身。

(1)定位到链表尾部,尝试把新节点放到后面;

(2)如果尾部变化了,则重新获取尾部,再重试;

出队

因为它不是阻塞队列,所以只有两个出队的方法,remove()和poll()。

public E remove() {
    E x = poll();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}

public E poll() {
    restartFromHead:
    for (;;) {
        // 尝试弹出链表的头节点
        for (Node h = head, p = h, q;;) {
            E item = p.item;
            // 如果节点的值不为空,并且将其更新为null成功了
            if (item != null && p.casItem(item, null)) {
                // 如果头节点变了,则不会走到这个分支
                // 会先走下面的分支拿到新的头节点
                // 这时候p就不等于h了,就更新头节点
                // 在updateHead()中会把head更新为新节点
                // 并让head的next指向其自己
                if (p != h) // hop two nodes at a time
                    updateHead(h, ((q = p.next) != null) ? q : p);
                // 上面的casItem()成功,就可以返回出队的元素了
                return item;
            }
            // 下面三个分支说明头节点变了
            // 且p的item肯定为null
            else if ((q = p.next) == null) {
                // 如果p的next为空,说明队列中没有元素了
                // 更新h为p,也就是空元素的节点
                updateHead(h, p);
                // 返回null
                return null;
            }
            else if (p == q)
                // 如果p等于p的next,说明p已经出队了,重试
                continue restartFromHead;
            else
                // 将p设置为p的next
                p = q;
        }
    }
}
// 更新头节点的方法
final void updateHead(Node h, Node p) {
    // 原子更新h为p成功后,延迟更新h的next为它自己
    // 这里用延迟更新是安全的,因为head节点已经变了
    // 只要入队出队的时候检查head有没有变化就行了,跟它的next关系不大
    if (h != p && casHead(h, p))
        h.lazySetNext(h);
}

出队的整个逻辑也是比较清晰的:

(1)定位到头节点,尝试更新其值为null;

(2)如果成功了,就成功出队;

(3)如果失败或者头节点变化了,就重新寻找头节点,并重试;

(4)整个出队过程没有一点阻塞相关的代码,所以出队的时候不会阻塞线程,没找到元素就返回null;

感谢各位的阅读!关于“java集合之ConcurrentLinkedQueue的示例代码”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


当前名称:java集合之ConcurrentLinkedQueue的示例代码-创新互联
网页路径:http://bjjierui.cn/article/hddgc.html

其他资讯