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;
}
}
}
}
Related examples in the same category