千家信息网

java集合之ConcurrentLinkedQueue的示例代码

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇文章给大家分享的是有关java集合之ConcurrentLinkedQueue的示例代码的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。问题(1)ConcurrentLi
千家信息网最后更新 2025年01月18日java集合之ConcurrentLinkedQueue的示例代码

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

问题

(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的示例代码"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0