Java并发容器之ConcurrentLinkedQueue源码分析

1 ConcurrentLinkedQueue概述

在并发编程中,我们经常使用线程安全的队列,Java为此提供了两种模式的队列:一个以ConcurrentLinkedQueue为代表的高性能队列 ;一个是以BlockingQueue接口为代表的阻塞队列。

阻塞队列使用一把锁(入队和出队共享一把锁)或者两把锁(入队、出队各一把锁)来实现线程安全,BlockingQueue是典型的代表;

非阻塞队列使用循环CAS操作保证数据的一致性,达到线程安全。

2 Node节点

ConcurrentLinkedQueue的底层数据结构是Node

private static class Node<E> {
        volatile E item;
        volatile Node<E> next;
        .
        .
        .
        .
}

Node节点主要包括数据域E和next指针。两者都用volatile关键字修饰,保证了内存中的可见性。

    private transient volatile Node<E> head;//头结点
    private transient volatile Node<E> tail;//尾节点

ConcurrentLinkedQueue还有这两个Node变量,说明使用头尾指针管理队列。

3 CAS操作

在多线程的环境下,对队列进行操作的时候难免会出现线程安全的问题;ConcurrentLinkedQueue保证线程安全的措施是对Node节点使用CAS操作

    private static class Node<E> {
        volatile E item;
        volatile Node<E> next;

        /**
         * Constructs a new node.  Uses relaxed write because item can
         * only be seen after publication via casNext.
         */
        Node(E item) {
            UNSAFE.putObject(this, itemOffset, item);
        }
        //更改数据E
        boolean casItem(E cmp, E val) {
            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }
        
        void lazySetNext(Node<E> val) {
            UNSAFE.putOrderedObject(this, nextOffset, val);
        }

        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;

        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);
            }
        }
    }

offer 操作

    public boolean offer(E e) {
        checkNotNull(e);//空指针判断
        
        //新建一个节点
        final Node<E> newNode = new Node<E>(e);
        //没有循环结束条件,循环执行队尾插入(tail可能指向尾节点的前一个节点)
        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                // 如果tail节点是尾节点,设置p节点的next节点为待入队的节点
                if (p.casNext(null, newNode)) {
                    //在循环体CAS操作成功会直接return返回,如果CAS操作失败的话就 在
                     for循环中不断重试直至成功
                     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;
        }
    }
    public E poll() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {//获取头结点
                E item = p.item;
                //如果头结点不为null 同时设置头结点数据为空成功
                if (item != null && p.casItem(item, null)) {
                    // Successful CAS is the linearization point
                    // for item to be removed from this queue.
                    if (p != h) // 如果p不等于h,说明表头发生改变,更新头结点
                        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;
            }
        }
    }

注意:tail节点不一定指向尾节点,head节点也不一定指向头结点,主要是为了减少CAS更新头结点的次数,增加了出队的效率(具体原因没有分析清楚,自己还未理解,待更新)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值