Java tutorial
/* * 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; } } } }