JAVA源码学习之集合-ConcurrentLinkedDeque

前言

上一章我们讲了ConcurrentLinkedQueue, 这章我们要讲ConcurrentLinkedDeque通过类的名字上看,通过名字,我猜想基本原理应该和ConcurrentLinkedQueue是一样的,除了数据结构式双向链表以外,下面就具体研究,来看下自己的猜想是不是正确的

正文

类的描述

  • public class ConcurrentLinkedDeque<E>
    extends AbstractCollection<E>
    implements Deque<E>, Serializable
    一个基于链表的无界双向队列。跨多个线程安全执行并发插入、删除和访问操作。当许多线程将共享对公共集合的访问权限时,ConcurrentLinkedDeque是一个合适的选择。。像大多数其他并行采集的实现,与大多数其他并发集合实现一样,此类不允许使用null元素。

    迭代器和spliterators是weakly consistent

    请注意,与大多数集合不同,size方法不是常数时间操作。由于这些数据的异步性质,确定当前元素数需要遍历元素,因此如果在遍历期间修改此集合,则可能会报告不准确的结果。此外,大容量操作addAllremoveAllretainAllcontainsAllequals,和toArray不保证是自动执行。例如,与addAll操作同时运行的迭代器可能只查看一些添加的元素。

    这个类和它的迭代器实现了Deque和Iterator接口的所有的可选方法。

    内存一致性效果:内存一致性影响:与其他并发集合一样,在将对象入ConcurrentLinkedDeque线程中的操作发生在从另一个线程中对这个ConcurrentLinkedQue访问或删除该元素的操作之前。

常量、变量、静态内部类

private transient volatile Node<E> head; 
//头节点
private transient volatile Node<E> head;
//尾节点
private transient volatile Node<E> tail;
//前一个结点静态变量和下一个节点静态变量
private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;

@SuppressWarnings("unchecked")
Node<E> prevTerminator() {
   return (Node<E>) PREV_TERMINATOR;
}

@SuppressWarnings("unchecked")
Node<E> nextTerminator() {
   return (Node<E>) NEXT_TERMINATOR;
}

static final class Node<E> {
        volatile Node<E> prev;
        volatile E item;
        volatile Node<E> next;

        Node() {  // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
        }

        /**
         * Constructs a new node.  Uses relaxed write because item can
         * only be seen after publication via casNext or casPrev.
         */
        Node(E item) {
            UNSAFE.putObject(this, itemOffset, item);
        }

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

        void lazySetPrev(Node<E> val) {
            UNSAFE.putOrderedObject(this, prevOffset, val);
        }

        boolean casPrev(Node<E> cmp, Node<E> val) {
            return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val);
        }

        // Unsafe mechanics

        private static final sun.misc.Unsafe UNSAFE;
        private static final long prevOffset;
        private static final long itemOffset;
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Node.class;
                prevOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("prev"));
                itemOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("item"));
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }
相较于ConcurrentLinkedQueue Node多了prev, 和用cas操作prev的几个方法的几个方法

队列中常用的方法

add

//头部插入
public void addFirst(E e) {
        linkFirst(e);
}

private void linkFirst(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        restartFromHead:
        for (;;)
            for (Node<E> h = head, p = h, q;;) {
                if ((q = p.prev) != null &&
                    (q = (p = q).prev) != null)
                    // Check for head updates every other hop.
                    // If p == q, we are sure to follow head instead.
                    p = (h != (h = head)) ? h : q;
                else if (p.next == p) // PREV_TERMINATOR
                    continue restartFromHead;
                else {
                    // p is first node
                    newNode.lazySetNext(p); // CAS piggyback
                    if (p.casPrev(null, newNode)) {
                        // Successful CAS is the linearization point
                        // for e to become an element of this deque,
                        // and for newNode to become "live".
                        if (p != h) // hop two nodes at a time
                            casHead(h, newNode);  // Failure is OK.
                        return;
                    }
                    // Lost CAS race to another thread; re-read prev
                }
            }
    }

//尾部插入
public void addLast(E e) {
        linkLast(e);
}


 private void linkLast(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        restartFromTail:
        for (;;)
            for (Node<E> t = tail, p = t, q;;) {
                if ((q = p.next) != null &&
                    (q = (p = q).next) != null)
                    // Check for tail updates every other hop.
                    // If p == q, we are sure to follow tail instead.
                    p = (t != (t = tail)) ? t : q;
                else if (p.prev == p) // NEXT_TERMINATOR
                    continue restartFromTail;
                else {
                    // p is last node
                    newNode.lazySetPrev(p); // CAS piggyback
                    if (p.casNext(null, newNode)) {
                        // Successful CAS is the linearization point
                        // for e to become an element of this deque,
                        // and for newNode to become "live".
                        if (p != t) // hop two nodes at a time
                            casTail(t, newNode);  // Failure is OK.
                        return;
                    }
                    // Lost CAS race to another thread; re-read next
                }
            }
    }

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

offer

    public boolean offerLast(E e) {
        linkLast(e);
        return true;
    }

 public boolean offerFirst(E e) {
        linkFirst(e);
        return true;
    }

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

通过add和offer我们可以更加确信一开始的猜想,如果想要理解linkFirst和linkLast方法内的内容,请参考上一章ConcurrentLinkedQueue的介绍

remove

public E remove()         { return removeFirst(); }

 public E removeFirst() {
        return screenNullResult(pollFirst());
    }

 public E removeLast() {
        return screenNullResult(pollLast());
    }

 private E screenNullResult(E v) {
        if (v == null)
            throw new NoSuchElementException();
        return v;
    }

//remove调用的poll

poll

public E pollFirst() {
        for (Node<E> p = first(); p != null; p = succ(p)) {
            E item = p.item;
            if (item != null && p.casItem(item, null)) {
                unlink(p);
                return item;
            }
        }
        return null;
    }

    Node<E> first() {
        restartFromHead:
        for (;;)
            for (Node<E> h = head, p = h, q;;) {
                if ((q = p.prev) != null &&
                    (q = (p = q).prev) != null)
                    // Check for head updates every other hop.
                    // If p == q, we are sure to follow head instead.
                    p = (h != (h = head)) ? h : q;
                else if (p == h
                         // It is possible that p is PREV_TERMINATOR,
                         // but if so, the CAS is guaranteed to fail.
                         || casHead(h, p))
                    return p;
                else
                    continue restartFromHead;
            }
    }

 final Node<E> succ(Node<E> p) {
        // TODO: should we skip deleted nodes here?
        Node<E> q = p.next;
        return (p == q) ? first() : q;
    }


  public E pollLast() {
        for (Node<E> p = last(); p != null; p = pred(p)) {
            E item = p.item;
            if (item != null && p.casItem(item, null)) {
                unlink(p);
                return item;
            }
        }
        return null;
    }

   Node<E> last() {
        restartFromTail:
        for (;;)
            for (Node<E> t = tail, p = t, q;;) {
                if ((q = p.next) != null &&
                    (q = (p = q).next) != null)
                    // Check for tail updates every other hop.
                    // If p == q, we are sure to follow tail instead.
                    p = (t != (t = tail)) ? t : q;
                else if (p == t
                         // It is possible that p is NEXT_TERMINATOR,
                         // but if so, the CAS is guaranteed to fail.
                         || casTail(t, p))
                    return p;
                else
                    continue restartFromTail;
            }
    }

  final Node<E> pred(Node<E> p) {
        Node<E> q = p.prev;
        return (p == q) ? last() : q;
    }

 

//细节请参考上一章ConcurrentLinkedQueue

peek

public E peek()           { return peekFirst(); }


  public E peekFirst() {
        for (Node<E> p = first(); p != null; p = succ(p)) {
            E item = p.item;
            if (item != null)
                return item;
        }
        return null;
    }

    public E peekLast() {
        for (Node<E> p = last(); p != null; p = pred(p)) {
            E item = p.item;
            if (item != null)
                return item;
        }
        return null;
    }

element

public E element()        { return getFirst(); }

 public E getFirst() {
        return screenNullResult(peekFirst());
    }

结论

1. ConcurrentLinkedDeQue是通过Node来存储元素的

2. 数据结构是双向链表, 所以是双向队列

3、 ConcurrentLinkedQueue 是通过Unsafe的cas乐观锁和volitile修饰head和tail来实现线程安全的

上一章                                                                                                                                   下一章​​​​​​​

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值