Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Concurrent Doubly LinkedList

/*  * Written by Doug Lea with assistance from members of JCP JSR-166  * Expert Group and released to the public domain, as explained at  * http://creativecommons.org/licenses/publicdomain  */ import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReference; /**  * A concurrent linked-list implementation of a {@link Deque} (double-ended  * queue). Concurrent insertion, removal, and access operations execute safely  * across multiple threads. Iterators are <i>weakly consistent</i>, returning  * elements reflecting the state of the deque at some point at or since the  * creation of the iterator. They do <em>not</em> throw {@link  * ConcurrentModificationException}, and may proceed concurrently with other  * operations.  *   * <p>  * This class and its iterators implement all of the <em>optional</em> methods  * of the {@link Collection} and {@link Iterator} interfaces. Like most other  * concurrent collection implementations, this class does not permit the use of  * <tt>null</tt> elements. because some null arguments and return values  * cannot be reliably distinguished from the absence of elements. Arbitrarily,  * the {@link Collection#remove} method is mapped to  * <tt>removeFirstOccurrence</tt>, and {@link Collection#add} is mapped to  * <tt>addLast</tt>.  *   * <p>  * Beware that, unlike in most collections, the <tt>size</tt> method is  * <em>NOT</em> a constant-time operation. Because of the asynchronous nature  * of these deques, determining the current number of elements requires a  * traversal of the elements.  *   * <p>  * This class is <tt>Serializable</tt>, but relies on default serialization  * mechanisms. Usually, it is a better idea for any serializable class using a  * <tt>ConcurrentLinkedDeque</tt> to instead serialize a snapshot of the  * elements obtained by method <tt>toArray</tt>.  *   * @author Doug Lea  * @param <E>  *            the type of elements held in this collection  */ public class ConcurrentDoublyLinkedList<E> extends AbstractCollection<E>     implements java.io.Serializable {   /*    * This is an adaptation of an algorithm described in Paul Martin's "A    * Practical Lock-Free Doubly-Linked List". Sun Labs Tech report. The basic    * idea is to primarily rely on next-pointers to ensure consistency.    * Prev-pointers are in part optimistic, reconstructed using forward    * pointers as needed. The main forward list uses a variant of HM-list    * algorithm similar to the one used in ConcurrentSkipListMap class, but a    * little simpler. It is also basically similar to the approach in Edya    * Ladan-Mozes and Nir Shavit "An Optimistic Approach to Lock-Free FIFO    * Queues" in DISC04.    *     * Quoting a summary in Paul Martin's tech report:    *     * All cleanups work to maintain these invariants: (1) forward pointers are    * the ground truth. (2) forward pointers to dead nodes can be improved by    * swinging them further forward around the dead node. (2.1) forward    * pointers are still correct when pointing to dead nodes, and forward    * pointers from dead nodes are left as they were when the node was deleted.    * (2.2) multiple dead nodes may point forward to the same node. (3)    * backward pointers were correct when they were installed (3.1) backward    * pointers are correct when pointing to any node which points forward to    * them, but since more than one forward pointer may point to them, the live    * one is best. (4) backward pointers that are out of date due to deletion    * point to a deleted node, and need to point further back until they point    * to the live node that points to their source. (5) backward pointers that    * are out of date due to insertion point too far backwards, so shortening    * their scope (by searching forward) fixes them. (6) backward pointers from    * a dead node cannot be "improved" since there may be no live node pointing    * forward to their origin. (However, it does no harm to try to improve them    * while racing with a deletion.)    *     *     * Notation guide for local variables n, b, f : a node, its predecessor, and    * successor s : some other successor    */      // Minor convenience utilities   /**    * Returns true if given reference is non-null and isn't a header, trailer,    * or marker.    *     * @param n    *            (possibly null) node    * @return true if n exists as a user node    */   private static boolean usable(Node<?> n) {     return n != null && !n.isSpecial();   }   /**    * Throws NullPointerException if argument is null    *     * @param v    *            the element    */   private static void checkNullArg(Object v) {     if (v == null)       throw new NullPointerException();   }   /**    * Returns element unless it is null, in which case throws    * NoSuchElementException.    *     * @param v    *            the element    * @return the element    */   private E screenNullResult(E v) {     if (v == null)       throw new NoSuchElementException();     return v;   }   /**    * Creates an array list and fills it with elements of this list. Used by    * toArray.    *     * @return the arrayList    */   private ArrayList<E> toArrayList() {     ArrayList<E> c = new ArrayList<E>();     for (Node<E> n = header.forward(); n != null; n = n.forward())       c.add(n.element);     return c;   }   // Fields and constructors   private static final long serialVersionUID = 876323262645176354L;   /**    * List header. First usable node is at header.forward().    */   private final Node<E> header;   /**    * List trailer. Last usable node is at trailer.back().    */   private final Node<E> trailer;   /**    * Constructs an empty deque.    */   public ConcurrentDoublyLinkedList() {     Node h = new Node(null, null, null);     Node t = new Node(null, null, h);     h.setNext(t);     header = h;     trailer = t;   }   /**    * Constructs a deque containing the elements of the specified collection,    * in the order they are returned by the collection's iterator.    *     * @param c    *            the collection whose elements are to be placed into this    *            deque.    * @throws NullPointerException    *             if <tt>c</tt> or any element within it is <tt>null</tt>    */   public ConcurrentDoublyLinkedList(Collection<? extends E> c) {     this();     addAll(c);   }   /**    * Prepends the given element at the beginning of this deque.    *     * @param o    *            the element to be inserted at the beginning of this deque.    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public void addFirst(E o) {     checkNullArg(o);     while (header.append(o) == null)       ;   }   /**    * Appends the given element to the end of this deque. This is identical in    * function to the <tt>add</tt> method.    *     * @param o    *            the element to be inserted at the end of this deque.    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public void addLast(E o) {     checkNullArg(o);     while (trailer.prepend(o) == null)       ;   }   /**    * Prepends the given element at the beginning of this deque.    *     * @param o    *            the element to be inserted at the beginning of this deque.    * @return <tt>true</tt> always    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public boolean offerFirst(E o) {     addFirst(o);     return true;   }   /**    * Appends the given element to the end of this deque. (Identical in    * function to the <tt>add</tt> method; included only for consistency.)    *     * @param o    *            the element to be inserted at the end of this deque.    * @return <tt>true</tt> always    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public boolean offerLast(E o) {     addLast(o);     return true;   }   /**    * Retrieves, but does not remove, the first element of this deque, or    * returns null if this deque is empty.    *     * @return the first element of this queue, or <tt>null</tt> if empty.    */   public E peekFirst() {     Node<E> n = header.successor();     return (n == null) ? null : n.element;   }   /**    * Retrieves, but does not remove, the last element of this deque, or    * returns null if this deque is empty.    *     * @return the last element of this deque, or <tt>null</tt> if empty.    */   public E peekLast() {     Node<E> n = trailer.predecessor();     return (n == null) ? null : n.element;   }   /**    * Returns the first element in this deque.    *     * @return the first element in this deque.    * @throws NoSuchElementException    *             if this deque is empty.    */   public E getFirst() {     return screenNullResult(peekFirst());   }   /**    * Returns the last element in this deque.    *     * @return the last element in this deque.    * @throws NoSuchElementException    *             if this deque is empty.    */   public E getLast() {     return screenNullResult(peekLast());   }   /**    * Retrieves and removes the first element of this deque, or returns null if    * this deque is empty.    *     * @return the first element of this deque, or <tt>null</tt> if empty.    */   public E pollFirst() {     for (;;) {       Node<E> n = header.successor();       if (!usable(n))         return null;       if (n.delete())         return n.element;     }   }   /**    * Retrieves and removes the last element of this deque, or returns null if    * this deque is empty.    *     * @return the last element of this deque, or <tt>null</tt> if empty.    */   public E pollLast() {     for (;;) {       Node<E> n = trailer.predecessor();       if (!usable(n))         return null;       if (n.delete())         return n.element;     }   }   /**    * Removes and returns the first element from this deque.    *     * @return the first element from this deque.    * @throws NoSuchElementException    *             if this deque is empty.    */   public E removeFirst() {     return screenNullResult(pollFirst());   }   /**    * Removes and returns the last element from this deque.    *     * @return the last element from this deque.    * @throws NoSuchElementException    *             if this deque is empty.    */   public E removeLast() {     return screenNullResult(pollLast());   }   // *** Queue and stack methods ***   public boolean offer(E e) {     return offerLast(e);   }   public boolean add(E e) {     return offerLast(e);   }   public E poll() {     return pollFirst();   }   public E remove() {     return removeFirst();   }   public E peek() {     return peekFirst();   }   public E element() {     return getFirst();   }   public void push(E e) {     addFirst(e);   }   public E pop() {     return removeFirst();   }   /**    * Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>,    * if such an element exists in this deque. If the deque does not contain    * the element, it is unchanged.    *     * @param o    *            element to be removed from this deque, if present.    * @return <tt>true</tt> if the deque contained the specified element.    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public boolean removeFirstOccurrence(Object o) {     checkNullArg(o);     for (;;) {       Node<E> n = header.forward();       for (;;) {         if (n == null)           return false;         if (o.equals(n.element)) {           if (n.delete())             return true;           else             break; // restart if interference         }         n = n.forward();       }     }   }   /**    * Removes the last element <tt>e</tt> such that <tt>o.equals(e)</tt>,    * if such an element exists in this deque. If the deque does not contain    * the element, it is unchanged.    *     * @param o    *            element to be removed from this deque, if present.    * @return <tt>true</tt> if the deque contained the specified element.    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public boolean removeLastOccurrence(Object o) {     checkNullArg(o);     for (;;) {       Node<E> s = trailer;       for (;;) {         Node<E> n = s.back();         if (s.isDeleted() || (n != null && n.successor() != s))           break; // restart if pred link is suspect.         if (n == null)           return false;         if (o.equals(n.element)) {           if (n.delete())             return true;           else             break; // restart if interference         }         s = n;       }     }   }   /**    * Returns <tt>true</tt> if this deque contains at least one element    * <tt>e</tt> such that <tt>o.equals(e)</tt>.    *     * @param o    *            element whose presence in this deque is to be tested.    * @return <tt>true</tt> if this deque contains the specified element.    */   public boolean contains(Object o) {     if (o == null)       return false;     for (Node<E> n = header.forward(); n != null; n = n.forward())       if (o.equals(n.element))         return true;     return false;   }   /**    * Returns <tt>true</tt> if this collection contains no elements.    * <p>    *     * @return <tt>true</tt> if this collection contains no elements.    */   public boolean isEmpty() {     return !usable(header.successor());   }   /**    * Returns the number of elements in this deque. If this deque contains more    * than <tt>Integer.MAX_VALUE</tt> elements, it returns    * <tt>Integer.MAX_VALUE</tt>.    *     * <p>    * Beware that, unlike in most collections, this method is <em>NOT</em> a    * constant-time operation. Because of the asynchronous nature of these    * deques, determining the current number of elements requires traversing    * them all to count them. Additionally, it is possible for the size to    * change during execution of this method, in which case the returned result    * will be inaccurate. Thus, this method is typically not very useful in    * concurrent applications.    *     * @return the number of elements in this deque.    */   public int size() {     long count = 0;     for (Node<E> n = header.forward(); n != null; n = n.forward())       ++count;     return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;   }   /**    * Removes the first element <tt>e</tt> such that <tt>o.equals(e)</tt>,    * if such an element exists in this deque. If the deque does not contain    * the element, it is unchanged.    *     * @param o    *            element to be removed from this deque, if present.    * @return <tt>true</tt> if the deque contained the specified element.    * @throws NullPointerException    *             if the specified element is <tt>null</tt>    */   public boolean remove(Object o) {     return removeFirstOccurrence(o);   }   /**    * Appends all of the elements in the specified collection to the end of    * this deque, in the order that they are returned by the specified    * collection's iterator. The behavior of this operation is undefined if the    * specified collection is modified while the operation is in progress.    * (This implies that the behavior of this call is undefined if the    * specified Collection is this deque, and this deque is nonempty.)    *     * @param c    *            the elements to be inserted into this deque.    * @return <tt>true</tt> if this deque changed as a result of the call.    * @throws NullPointerException    *             if <tt>c</tt> or any element within it is <tt>null</tt>    */   public boolean addAll(Collection<? extends E> c) {     Iterator<? extends E> it = c.iterator();     if (!it.hasNext())       return false;     do {       addLast(it.next());     } while (it.hasNext());     return true;   }   /**    * Removes all of the elements from this deque.    */   public void clear() {     while (pollFirst() != null)       ;   }   /**    * Returns an array containing all of the elements in this deque in the    * correct order.    *     * @return an array containing all of the elements in this deque in the    *         correct order.    */   public Object[] toArray() {     return toArrayList().toArray();   }   /**    * Returns an array containing all of the elements in this deque in the    * correct order; the runtime type of the returned array is that of the    * specified array. If the deque fits in the specified array, it is returned    * therein. Otherwise, a new array is allocated with the runtime type of the    * specified array and the size of this deque.    * <p>    *     * If the deque fits in the specified array with room to spare (i.e., the    * array has more elements than the deque), the element in the array    * immediately following the end of the collection is set to null. This is    * useful in determining the length of the deque <i>only</i> if the caller    * knows that the deque does not contain any null elements.    *     * @param a    *            the array into which the elements of the deque are to be    *            stored, if it is big enough; otherwise, a new array of the    *            same runtime type is allocated for this purpose.    * @return an array containing the elements of the deque.    * @throws ArrayStoreException    *             if the runtime type of a is not a supertype of the runtime    *             type of every element in this deque.    * @throws NullPointerException    *             if the specified array is null.    */   public <T> T[] toArray(T[] a) {     return toArrayList().toArray(a);   }   /**    * Returns a weakly consistent iterator over the elements in this deque, in    * first-to-last order. The <tt>next</tt> method returns elements    * reflecting the state of the deque at some point at or since the creation    * of the iterator. The method does <em>not</em> throw    * {@link ConcurrentModificationException}, and may proceed concurrently    * with other operations.    *     * @return an iterator over the elements in this deque    */   public Iterator<E> iterator() {     return new CLDIterator();   }   final class CLDIterator implements Iterator<E> {     Node<E> last;     Node<E> next = header.forward();     public boolean hasNext() {       return next != null;     }     public E next() {       Node<E> l = last = next;       if (l == null)         throw new NoSuchElementException();       next = next.forward();       return l.element;     }     public void remove() {       Node<E> l = last;       if (l == null)         throw new IllegalStateException();       while (!l.delete() && !l.isDeleted())         ;     }   } } /**  * Linked Nodes. As a minor efficiency hack, this class opportunistically  * inherits from AtomicReference, with the atomic ref used as the "next"  * link.  *   * Nodes are in doubly-linked lists. There are three kinds of special nodes,  * distinguished by: * The list header has a null prev link * The list  * trailer has a null next link * A deletion marker has a prev link pointing  * to itself. All three kinds of special nodes have null element fields.  *   * Regular nodes have non-null element, next, and prev fields. To avoid  * visible inconsistencies when deletions overlap element replacement,  * replacements are done by replacing the node, not just setting the  * element.  *   * Nodes can be traversed by read-only ConcurrentLinkedDeque class  * operations just by following raw next pointers, so long as they ignore  * any special nodes seen along the way. (This is automated in method  * forward.) However, traversal using prev pointers is not guaranteed to see  * all live nodes since a prev pointer of a deleted node can become  * unrecoverably stale.  */  class Node<E> extends AtomicReference<Node<E>> {   private volatile Node<E> prev;   final E element;   /** Creates a node with given contents */   Node(E element, Node<E> next, Node<E> prev) {     super(next);     this.prev = prev;     this.element = element;   }   /** Creates a marker node with given successor */   Node(Node<E> next) {     super(next);     this.prev = this;     this.element = null;   }   /**    * Gets next link (which is actually the value held as atomic    * reference).    */   private Node<E> getNext() {     return get();   }   /**    * Sets next link    *     * @param n    *            the next node    */   void setNext(Node<E> n) {     set(n);   }   /**    * compareAndSet next link    */   private boolean casNext(Node<E> cmp, Node<E> val) {     return compareAndSet(cmp, val);   }   /**    * Gets prev link    */   private Node<E> getPrev() {     return prev;   }   /**    * Sets prev link    *     * @param b    *            the previous node    */   void setPrev(Node<E> b) {     prev = b;   }   /**    * Returns true if this is a header, trailer, or marker node    */   boolean isSpecial() {     return element == null;   }   /**    * Returns true if this is a trailer node    */   boolean isTrailer() {     return getNext() == null;   }   /**    * Returns true if this is a header node    */   boolean isHeader() {     return getPrev() == null;   }   /**    * Returns true if this is a marker node    */   boolean isMarker() {     return getPrev() == this;   }   /**    * Returns true if this node is followed by a marker, meaning that it is    * deleted.    *     * @return true if this node is deleted    */   boolean isDeleted() {     Node<E> f = getNext();     return f != null && f.isMarker();   }   /**    * Returns next node, ignoring deletion marker    */   private Node<E> nextNonmarker() {     Node<E> f = getNext();     return (f == null || !f.isMarker()) ? f : f.getNext();   }   /**    * Returns the next non-deleted node, swinging next pointer around any    * encountered deleted nodes, and also patching up successor''s prev    * link to point back to this. Returns null if this node is trailer so    * has no successor.    *     * @return successor, or null if no such    */   Node<E> successor() {     Node<E> f = nextNonmarker();     for (;;) {       if (f == null)         return null;       if (!f.isDeleted()) {         if (f.getPrev() != this && !isDeleted())           f.setPrev(this); // relink f's prev         return f;       }       Node<E> s = f.nextNonmarker();       if (f == getNext())         casNext(f, s); // unlink f       f = s;     }   }   /**    * Returns the apparent predecessor of target by searching forward for    * it starting at this node, patching up pointers while traversing. Used    * by predecessor().    *     * @return target's predecessor, or null if not found    */   private Node<E> findPredecessorOf(Node<E> target) {     Node<E> n = this;     for (;;) {       Node<E> f = n.successor();       if (f == target)         return n;       if (f == null)         return null;       n = f;     }   }   /**    * Returns the previous non-deleted node, patching up pointers as    * needed. Returns null if this node is header so has no successor. May    * also return null if this node is deleted, so doesn't have a distinct    * predecessor.    *     * @return predecessor or null if not found    */   Node<E> predecessor() {     Node<E> n = this;     for (;;) {       Node<E> b = n.getPrev();       if (b == null)         return n.findPredecessorOf(this);       Node<E> s = b.getNext();       if (s == this)         return b;       if (s == null || !s.isMarker()) {         Node<E> p = b.findPredecessorOf(this);         if (p != null)           return p;       }       n = b;     }   }   /**    * Returns the next node containing a nondeleted user element. Use for    * forward list traversal.    *     * @return successor, or null if no such    */   Node<E> forward() {     Node<E> f = successor();     return (f == null || f.isSpecial()) ? null : f;   }   /**    * Returns previous node containing a nondeleted user element, if    * possible. Use for backward list traversal, but beware that if this    * method is called from a deleted node, it might not be able to    * determine a usable predecessor.    *     * @return predecessor, or null if no such could be found    */   Node<E> back() {     Node<E> f = predecessor();     return (f == null || f.isSpecial()) ? null : f;   }   /**    * Tries to insert a node holding element as successor, failing if this    * node is deleted.    *     * @param element    *            the element    * @return the new node, or null on failure.    */   Node<E> append(E element) {     for (;;) {       Node<E> f = getNext();       if (f == null || f.isMarker())         return null;       Node<E> x = new Node<E>(element, f, this);       if (casNext(f, x)) {         f.setPrev(x); // optimistically link         return x;       }     }   }   /**    * Tries to insert a node holding element as predecessor, failing if no    * live predecessor can be found to link to.    *     * @param element    *            the element    * @return the new node, or null on failure.    */   Node<E> prepend(E element) {     for (;;) {       Node<E> b = predecessor();       if (b == null)         return null;       Node<E> x = new Node<E>(element, this, b);       if (b.casNext(this, x)) {         setPrev(x); // optimistically link         return x;       }     }   }   /**    * Tries to mark this node as deleted, failing if already deleted or if    * this node is header or trailer    *     * @return true if successful    */   boolean delete() {     Node<E> b = getPrev();     Node<E> f = getNext();     if (b != null && f != null && !f.isMarker()         && casNext(f, new Node(f))) {       if (b.casNext(this, f))         f.setPrev(b);       return true;     }     return false;   }   /**    * Tries to insert a node holding element to replace this node. failing    * if already deleted.    *     * @param newElement    *            the new element    * @return the new node, or null on failure.    */   Node<E> replace(E newElement) {     for (;;) {       Node<E> b = getPrev();       Node<E> f = getNext();       if (b == null || f == null || f.isMarker())         return null;       Node<E> x = new Node<E>(newElement, f, b);       if (casNext(f, new Node(x))) {         b.successor(); // to relink b         x.successor(); // to relink f         return x;       }     }   } }