6981113: Add ConcurrentLinkedDeque
Extend techniques developed for ConcurrentLinkedQueue and LinkedTransferQueue to implement a non-blocking concurrent Deque with interior removes. Reviewed-by: martin, dholmes, chegar
This commit is contained in:
parent
11bbc5d5bc
commit
94ffffd5cb
@ -272,6 +272,7 @@ JAVA_JAVA_java = \
|
||||
java/util/concurrent/CancellationException.java \
|
||||
java/util/concurrent/CompletionService.java \
|
||||
java/util/concurrent/ConcurrentHashMap.java \
|
||||
java/util/concurrent/ConcurrentLinkedDeque.java \
|
||||
java/util/concurrent/ConcurrentLinkedQueue.java \
|
||||
java/util/concurrent/ConcurrentMap.java \
|
||||
java/util/concurrent/ConcurrentNavigableMap.java \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,9 +28,9 @@
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* 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
|
||||
* Written by Doug Lea and Martin Buchholz with assistance from members of
|
||||
* JCP JSR-166 Expert Group and released to the public domain, as explained
|
||||
* at http://creativecommons.org/licenses/publicdomain
|
||||
*/
|
||||
|
||||
package java.util.concurrent;
|
||||
@ -53,7 +53,8 @@ import java.util.Queue;
|
||||
* operations obtain elements at the head of the queue.
|
||||
* A {@code ConcurrentLinkedQueue} is an appropriate choice when
|
||||
* many threads will share access to a common collection.
|
||||
* This queue does not permit {@code null} elements.
|
||||
* Like most other concurrent collection implementations, this class
|
||||
* does not permit the use of {@code null} elements.
|
||||
*
|
||||
* <p>This implementation employs an efficient "wait-free"
|
||||
* algorithm based on one described in <a
|
||||
@ -61,14 +62,20 @@ import java.util.Queue;
|
||||
* Fast, and Practical Non-Blocking and Blocking Concurrent Queue
|
||||
* Algorithms</a> by Maged M. Michael and Michael L. Scott.
|
||||
*
|
||||
* <p>Iterators are <i>weakly consistent</i>, returning elements
|
||||
* reflecting the state of the queue 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. Elements contained in the queue since the creation
|
||||
* of the iterator will be returned exactly once.
|
||||
*
|
||||
* <p>Beware that, unlike in most collections, the {@code size} method
|
||||
* is <em>NOT</em> a constant-time operation. Because of the
|
||||
* asynchronous nature of these queues, determining the current number
|
||||
* of elements requires a traversal of the elements.
|
||||
*
|
||||
* <p>This class and its iterator implement all of the
|
||||
* <em>optional</em> methods of the {@link Collection} and {@link
|
||||
* Iterator} interfaces.
|
||||
* <p>This class and its iterator implement all of the <em>optional</em>
|
||||
* methods of the {@link Queue} and {@link Iterator} interfaces.
|
||||
*
|
||||
* <p>Memory consistency effects: As with other concurrent
|
||||
* collections, actions in a thread prior to placing an object into a
|
||||
@ -132,9 +139,10 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
*
|
||||
* Both head and tail are permitted to lag. In fact, failing to
|
||||
* update them every time one could is a significant optimization
|
||||
* (fewer CASes). This is controlled by local "hops" variables
|
||||
* that only trigger helping-CASes after experiencing multiple
|
||||
* lags.
|
||||
* (fewer CASes). As with LinkedTransferQueue (see the internal
|
||||
* documentation for that class), we use a slack threshold of two;
|
||||
* that is, we update head/tail when the current pointer appears
|
||||
* to be two or more steps away from the first/last node.
|
||||
*
|
||||
* Since head and tail are updated concurrently and independently,
|
||||
* it is possible for tail to lag behind head (why not)?
|
||||
@ -148,8 +156,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* this is merely an optimization.
|
||||
*
|
||||
* When constructing a Node (before enqueuing it) we avoid paying
|
||||
* for a volatile write to item by using lazySet instead of a
|
||||
* normal write. This allows the cost of enqueue to be
|
||||
* for a volatile write to item by using Unsafe.putObject instead
|
||||
* of a normal write. This allows the cost of enqueue to be
|
||||
* "one-and-a-half" CASes.
|
||||
*
|
||||
* Both head and tail may or may not point to a Node with a
|
||||
@ -161,38 +169,25 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
|
||||
private static class Node<E> {
|
||||
private volatile E item;
|
||||
private volatile Node<E> next;
|
||||
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) {
|
||||
// Piggyback on imminent casNext()
|
||||
lazySetItem(item);
|
||||
}
|
||||
|
||||
E getItem() {
|
||||
return item;
|
||||
UNSAFE.putObject(this, itemOffset, item);
|
||||
}
|
||||
|
||||
boolean casItem(E cmp, E val) {
|
||||
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
|
||||
}
|
||||
|
||||
void setItem(E val) {
|
||||
item = val;
|
||||
}
|
||||
|
||||
void lazySetItem(E val) {
|
||||
UNSAFE.putOrderedObject(this, itemOffset, val);
|
||||
}
|
||||
|
||||
void lazySetNext(Node<E> val) {
|
||||
UNSAFE.putOrderedObject(this, nextOffset, val);
|
||||
}
|
||||
|
||||
Node<E> getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
boolean casNext(Node<E> cmp, Node<E> val) {
|
||||
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
|
||||
}
|
||||
@ -219,7 +214,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* - it is permitted for tail to lag behind head, that is, for tail
|
||||
* to not be reachable from head!
|
||||
*/
|
||||
private transient volatile Node<E> head = new Node<E>(null);
|
||||
private transient volatile Node<E> head;
|
||||
|
||||
/**
|
||||
* A node from which the last node on list (that is, the unique
|
||||
@ -233,25 +228,41 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* to not be reachable from head!
|
||||
* - tail.next may or may not be self-pointing to tail.
|
||||
*/
|
||||
private transient volatile Node<E> tail = head;
|
||||
private transient volatile Node<E> tail;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@code ConcurrentLinkedQueue} that is initially empty.
|
||||
*/
|
||||
public ConcurrentLinkedQueue() {}
|
||||
public ConcurrentLinkedQueue() {
|
||||
head = tail = new Node<E>(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ConcurrentLinkedQueue}
|
||||
* initially containing the elements of the given collection,
|
||||
* added in traversal order of the collection's iterator.
|
||||
*
|
||||
* @param c the collection of elements to initially contain
|
||||
* @throws NullPointerException if the specified collection or any
|
||||
* of its elements are null
|
||||
*/
|
||||
public ConcurrentLinkedQueue(Collection<? extends E> c) {
|
||||
for (E e : c)
|
||||
add(e);
|
||||
Node<E> h = null, t = null;
|
||||
for (E e : c) {
|
||||
checkNotNull(e);
|
||||
Node<E> newNode = new Node<E>(e);
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
if (h == null)
|
||||
h = t = new Node<E>(null);
|
||||
head = h;
|
||||
tail = t;
|
||||
}
|
||||
|
||||
// Have to override just to update the javadoc
|
||||
@ -266,13 +277,6 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
return offer(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't bother to update head or tail pointers if fewer than
|
||||
* HOPS links from "true" location. We assume that volatile
|
||||
* writes are significantly more expensive than volatile reads.
|
||||
*/
|
||||
private static final int HOPS = 1;
|
||||
|
||||
/**
|
||||
* Try to CAS head to p. If successful, repoint old head to itself
|
||||
* as sentinel for succ(), below.
|
||||
@ -288,7 +292,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* stale pointer that is now off the list.
|
||||
*/
|
||||
final Node<E> succ(Node<E> p) {
|
||||
Node<E> next = p.getNext();
|
||||
Node<E> next = p.next;
|
||||
return (p == next) ? head : next;
|
||||
}
|
||||
|
||||
@ -299,68 +303,75 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* @throws NullPointerException if the specified element is null
|
||||
*/
|
||||
public boolean offer(E e) {
|
||||
if (e == null) throw new NullPointerException();
|
||||
Node<E> n = new Node<E>(e);
|
||||
retry:
|
||||
for (;;) {
|
||||
Node<E> t = tail;
|
||||
Node<E> p = t;
|
||||
for (int hops = 0; ; hops++) {
|
||||
Node<E> next = succ(p);
|
||||
if (next != null) {
|
||||
if (hops > HOPS && t != tail)
|
||||
continue retry;
|
||||
p = next;
|
||||
} else if (p.casNext(null, n)) {
|
||||
if (hops >= HOPS)
|
||||
casTail(t, n); // Failure is OK.
|
||||
checkNotNull(e);
|
||||
final Node<E> newNode = new Node<E>(e);
|
||||
|
||||
for (Node<E> t = tail, p = t;;) {
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (p.casNext(null, newNode)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for e to become an element of this queue,
|
||||
// and for newNode to become "live".
|
||||
if (p != t) // hop two nodes at a time
|
||||
casTail(t, newNode); // Failure is OK.
|
||||
return true;
|
||||
} else {
|
||||
p = succ(p);
|
||||
}
|
||||
// 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() {
|
||||
Node<E> h = head;
|
||||
Node<E> p = h;
|
||||
for (int hops = 0; ; hops++) {
|
||||
E item = p.getItem();
|
||||
restartFromHead:
|
||||
for (;;) {
|
||||
for (Node<E> h = head, p = h, q;;) {
|
||||
E item = p.item;
|
||||
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
if (hops >= HOPS) {
|
||||
Node<E> q = p.getNext();
|
||||
updateHead(h, (q != null) ? q : p);
|
||||
if (item != null && p.casItem(item, null)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for item to be removed from this queue.
|
||||
if (p != h) // hop two nodes at a time
|
||||
updateHead(h, ((q = p.next) != null) ? q : p);
|
||||
return item;
|
||||
}
|
||||
return item;
|
||||
else if ((q = p.next) == null) {
|
||||
updateHead(h, p);
|
||||
return null;
|
||||
}
|
||||
else if (p == q)
|
||||
continue restartFromHead;
|
||||
else
|
||||
p = q;
|
||||
}
|
||||
Node<E> next = succ(p);
|
||||
if (next == null) {
|
||||
updateHead(h, p);
|
||||
break;
|
||||
}
|
||||
p = next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public E peek() {
|
||||
Node<E> h = head;
|
||||
Node<E> p = h;
|
||||
E item;
|
||||
restartFromHead:
|
||||
for (;;) {
|
||||
item = p.getItem();
|
||||
if (item != null)
|
||||
break;
|
||||
Node<E> next = succ(p);
|
||||
if (next == null) {
|
||||
break;
|
||||
for (Node<E> h = head, p = h, q;;) {
|
||||
E item = p.item;
|
||||
if (item != null || (q = p.next) == null) {
|
||||
updateHead(h, p);
|
||||
return item;
|
||||
}
|
||||
else if (p == q)
|
||||
continue restartFromHead;
|
||||
else
|
||||
p = q;
|
||||
}
|
||||
p = next;
|
||||
}
|
||||
updateHead(h, p);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,24 +383,20 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* of losing a race to a concurrent poll().
|
||||
*/
|
||||
Node<E> first() {
|
||||
Node<E> h = head;
|
||||
Node<E> p = h;
|
||||
Node<E> result;
|
||||
restartFromHead:
|
||||
for (;;) {
|
||||
E item = p.getItem();
|
||||
if (item != null) {
|
||||
result = p;
|
||||
break;
|
||||
for (Node<E> h = head, p = h, q;;) {
|
||||
boolean hasItem = (p.item != null);
|
||||
if (hasItem || (q = p.next) == null) {
|
||||
updateHead(h, p);
|
||||
return hasItem ? p : null;
|
||||
}
|
||||
else if (p == q)
|
||||
continue restartFromHead;
|
||||
else
|
||||
p = q;
|
||||
}
|
||||
Node<E> next = succ(p);
|
||||
if (next == null) {
|
||||
result = null;
|
||||
break;
|
||||
}
|
||||
p = next;
|
||||
}
|
||||
updateHead(h, p);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,18 +417,20 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
* <em>NOT</em> a constant-time operation. Because of the
|
||||
* asynchronous nature of these queues, determining the current
|
||||
* number of elements requires an O(n) traversal.
|
||||
* Additionally, if elements are added or removed during execution
|
||||
* of this method, the returned result may be inaccurate. Thus,
|
||||
* this method is typically not very useful in concurrent
|
||||
* applications.
|
||||
*
|
||||
* @return the number of elements in this queue
|
||||
*/
|
||||
public int size() {
|
||||
int count = 0;
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
if (p.getItem() != null) {
|
||||
// Collections.size() spec says to max out
|
||||
for (Node<E> p = first(); p != null; p = succ(p))
|
||||
if (p.item != null)
|
||||
// Collection.size() spec says to max out
|
||||
if (++count == Integer.MAX_VALUE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -436,9 +445,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
public boolean contains(Object o) {
|
||||
if (o == null) return false;
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.getItem();
|
||||
if (item != null &&
|
||||
o.equals(item))
|
||||
E item = p.item;
|
||||
if (item != null && o.equals(item))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -459,7 +467,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
if (o == null) return false;
|
||||
Node<E> pred = null;
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.getItem();
|
||||
E item = p.item;
|
||||
if (item != null &&
|
||||
o.equals(item) &&
|
||||
p.casItem(item, null)) {
|
||||
@ -473,6 +481,69 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends all of the elements in the specified collection to the end of
|
||||
* this queue, in the order that they are returned by the specified
|
||||
* collection's iterator. Attempts to {@code addAll} of a queue to
|
||||
* itself result in {@code IllegalArgumentException}.
|
||||
*
|
||||
* @param c the elements to be inserted into this queue
|
||||
* @return {@code true} if this queue changed as a result of the call
|
||||
* @throws NullPointerException if the specified collection or any
|
||||
* of its elements are null
|
||||
* @throws IllegalArgumentException if the collection is this queue
|
||||
*/
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
if (c == this)
|
||||
// As historically specified in AbstractQueue#addAll
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> beginningOfTheEnd = null, last = null;
|
||||
for (E e : c) {
|
||||
checkNotNull(e);
|
||||
Node<E> newNode = new Node<E>(e);
|
||||
if (beginningOfTheEnd == null)
|
||||
beginningOfTheEnd = last = newNode;
|
||||
else {
|
||||
last.lazySetNext(newNode);
|
||||
last = newNode;
|
||||
}
|
||||
}
|
||||
if (beginningOfTheEnd == null)
|
||||
return false;
|
||||
|
||||
// Atomically append the chain at the tail of this collection
|
||||
for (Node<E> t = tail, p = t;;) {
|
||||
Node<E> q = p.next;
|
||||
if (q == null) {
|
||||
// p is last node
|
||||
if (p.casNext(null, beginningOfTheEnd)) {
|
||||
// Successful CAS is the linearization point
|
||||
// for all elements to be added to this queue.
|
||||
if (!casTail(t, last)) {
|
||||
// Try a little harder to update tail,
|
||||
// since we may be adding many elements.
|
||||
t = tail;
|
||||
if (last.next == null)
|
||||
casTail(t, last);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all of the elements in this queue, in
|
||||
* proper sequence.
|
||||
@ -490,7 +561,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
// Use ArrayList to deal with resizing.
|
||||
ArrayList<E> al = new ArrayList<E>();
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
E item = p.getItem();
|
||||
E item = p.item;
|
||||
if (item != null)
|
||||
al.add(item);
|
||||
}
|
||||
@ -539,7 +610,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
int k = 0;
|
||||
Node<E> p;
|
||||
for (p = first(); p != null && k < a.length; p = succ(p)) {
|
||||
E item = p.getItem();
|
||||
E item = p.item;
|
||||
if (item != null)
|
||||
a[k++] = (T)item;
|
||||
}
|
||||
@ -552,7 +623,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
// If won't fit, use ArrayList version
|
||||
ArrayList<E> al = new ArrayList<E>();
|
||||
for (Node<E> q = first(); q != null; q = succ(q)) {
|
||||
E item = q.getItem();
|
||||
E item = q.item;
|
||||
if (item != null)
|
||||
al.add(item);
|
||||
}
|
||||
@ -561,7 +632,9 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
|
||||
/**
|
||||
* Returns an iterator over the elements in this queue in proper sequence.
|
||||
* The returned iterator is a "weakly consistent" iterator that
|
||||
* The elements will be returned in order from first (head) to last (tail).
|
||||
*
|
||||
* <p>The returned {@code Iterator} is a "weakly consistent" iterator that
|
||||
* will never throw {@link java.util.ConcurrentModificationException
|
||||
* ConcurrentModificationException},
|
||||
* and guarantees to traverse elements as they existed upon
|
||||
@ -620,7 +693,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
nextItem = null;
|
||||
return x;
|
||||
}
|
||||
E item = p.getItem();
|
||||
E item = p.item;
|
||||
if (item != null) {
|
||||
nextNode = p;
|
||||
nextItem = item;
|
||||
@ -648,13 +721,13 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
Node<E> l = lastRet;
|
||||
if (l == null) throw new IllegalStateException();
|
||||
// rely on a future traversal to relink.
|
||||
l.setItem(null);
|
||||
l.item = null;
|
||||
lastRet = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the state to a stream (that is, serialize it).
|
||||
* Saves the state to a stream (that is, serializes it).
|
||||
*
|
||||
* @serialData All of the elements (each an {@code E}) in
|
||||
* the proper order, followed by a null
|
||||
@ -668,7 +741,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
|
||||
// Write out all elements in the proper order.
|
||||
for (Node<E> p = first(); p != null; p = succ(p)) {
|
||||
Object item = p.getItem();
|
||||
Object item = p.item;
|
||||
if (item != null)
|
||||
s.writeObject(item);
|
||||
}
|
||||
@ -678,25 +751,40 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstitute the Queue instance from a stream (that is,
|
||||
* deserialize it).
|
||||
* Reconstitutes the instance from a stream (that is, deserializes it).
|
||||
* @param s the stream
|
||||
*/
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
// Read in capacity, and any hidden stuff
|
||||
s.defaultReadObject();
|
||||
head = new Node<E>(null);
|
||||
tail = head;
|
||||
// Read in all elements and place in queue
|
||||
for (;;) {
|
||||
|
||||
// Read in elements until trailing null sentinel found
|
||||
Node<E> h = null, t = null;
|
||||
Object item;
|
||||
while ((item = s.readObject()) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
E item = (E)s.readObject();
|
||||
if (item == null)
|
||||
break;
|
||||
else
|
||||
offer(item);
|
||||
Node<E> newNode = new Node<E>((E) item);
|
||||
if (h == null)
|
||||
h = t = newNode;
|
||||
else {
|
||||
t.lazySetNext(newNode);
|
||||
t = newNode;
|
||||
}
|
||||
}
|
||||
if (h == null)
|
||||
h = t = new Node<E>(null);
|
||||
head = h;
|
||||
tail = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws NullPointerException if argument is null.
|
||||
*
|
||||
* @param v the element
|
||||
*/
|
||||
private static void checkNotNull(Object v) {
|
||||
if (v == null)
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
@ -715,10 +803,6 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
|
||||
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
|
||||
}
|
||||
|
||||
private void lazySetHead(Node<E> val) {
|
||||
UNSAFE.putOrderedObject(this, headOffset, val);
|
||||
}
|
||||
|
||||
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
|
||||
String field, Class<?> klazz) {
|
||||
try {
|
||||
|
@ -173,6 +173,11 @@ public class BiggernYours {
|
||||
new ConcurrentHashMap() {
|
||||
public int size() {return randomize(super.size());}});
|
||||
|
||||
testCollections(
|
||||
new ConcurrentLinkedDeque(),
|
||||
new ConcurrentLinkedDeque() {
|
||||
public int size() {return randomize(super.size());}});
|
||||
|
||||
testCollections(
|
||||
new ConcurrentLinkedQueue(),
|
||||
new ConcurrentLinkedQueue() {
|
||||
|
@ -48,6 +48,7 @@ public class IteratorAtEnd {
|
||||
testCollection(new PriorityQueue());
|
||||
testCollection(new LinkedBlockingQueue());
|
||||
testCollection(new ArrayBlockingQueue(100));
|
||||
testCollection(new ConcurrentLinkedDeque());
|
||||
testCollection(new ConcurrentLinkedQueue());
|
||||
testCollection(new LinkedTransferQueue());
|
||||
|
||||
|
@ -75,6 +75,7 @@ public class MOAT {
|
||||
testCollection(new ArrayBlockingQueue<Integer>(20));
|
||||
testCollection(new LinkedBlockingQueue<Integer>(20));
|
||||
testCollection(new LinkedBlockingDeque<Integer>(20));
|
||||
testCollection(new ConcurrentLinkedDeque<Integer>());
|
||||
testCollection(new ConcurrentLinkedQueue<Integer>());
|
||||
testCollection(new LinkedTransferQueue<Integer>());
|
||||
testCollection(new ConcurrentSkipListSet<Integer>());
|
||||
@ -431,8 +432,9 @@ public class MOAT {
|
||||
q.poll();
|
||||
equal(q.size(), 4);
|
||||
checkFunctionalInvariants(q);
|
||||
if ((q instanceof LinkedBlockingQueue) ||
|
||||
(q instanceof LinkedBlockingDeque) ||
|
||||
if ((q instanceof LinkedBlockingQueue) ||
|
||||
(q instanceof LinkedBlockingDeque) ||
|
||||
(q instanceof ConcurrentLinkedDeque) ||
|
||||
(q instanceof ConcurrentLinkedQueue)) {
|
||||
testQueueIteratorRemove(q);
|
||||
}
|
||||
|
@ -235,6 +235,7 @@ public class RacingCollections {
|
||||
new ArrayList<Queue<Integer>>(newConcurrentDeques());
|
||||
list.add(new LinkedBlockingQueue<Integer>(10));
|
||||
list.add(new LinkedTransferQueue<Integer>());
|
||||
list.add(new ConcurrentLinkedQueue<Integer>());
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -248,6 +249,7 @@ public class RacingCollections {
|
||||
private static List<Deque<Integer>> newConcurrentDeques() {
|
||||
List<Deque<Integer>> list = new ArrayList<Deque<Integer>>();
|
||||
list.add(new LinkedBlockingDeque<Integer>(10));
|
||||
list.add(new ConcurrentLinkedDeque<Integer>());
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ public class ChorusLine {
|
||||
deqs.add(new ArrayDeque<Integer>());
|
||||
deqs.add(new LinkedList<Integer>());
|
||||
deqs.add(new LinkedBlockingDeque<Integer>());
|
||||
deqs.add(new ConcurrentLinkedDeque<Integer>());
|
||||
|
||||
equal(deqs);
|
||||
|
||||
|
@ -55,6 +55,7 @@ public class ConcurrentQueueLoops {
|
||||
|
||||
Collection<Queue<Integer>> concurrentQueues() {
|
||||
List<Queue<Integer>> queues = new ArrayList<Queue<Integer>>();
|
||||
queues.add(new ConcurrentLinkedDeque<Integer>());
|
||||
queues.add(new ConcurrentLinkedQueue<Integer>());
|
||||
queues.add(new ArrayBlockingQueue<Integer>(items, false));
|
||||
//queues.add(new ArrayBlockingQueue<Integer>(count, true));
|
||||
@ -105,7 +106,7 @@ public class ConcurrentQueueLoops {
|
||||
final Queue<Integer> queue;
|
||||
final CyclicBarrier barrier;
|
||||
int items;
|
||||
Stage (Queue<Integer> q, CyclicBarrier b, int items) {
|
||||
Stage(Queue<Integer> q, CyclicBarrier b, int items) {
|
||||
queue = q;
|
||||
barrier = b;
|
||||
this.items = items;
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@ -62,6 +63,7 @@ public class GCRetention {
|
||||
|
||||
Collection<Queue<Boolean>> queues() {
|
||||
List<Queue<Boolean>> queues = new ArrayList<Queue<Boolean>>();
|
||||
queues.add(new ConcurrentLinkedDeque<Boolean>());
|
||||
queues.add(new ConcurrentLinkedQueue<Boolean>());
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, false));
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, true));
|
||||
|
@ -48,6 +48,7 @@ public class IteratorWeakConsistency {
|
||||
test(new LinkedBlockingQueue(20));
|
||||
test(new LinkedBlockingDeque());
|
||||
test(new LinkedBlockingDeque(20));
|
||||
test(new ConcurrentLinkedDeque());
|
||||
test(new ConcurrentLinkedQueue());
|
||||
test(new LinkedTransferQueue());
|
||||
// Other concurrent queues (e.g. ArrayBlockingQueue) do not
|
||||
|
@ -55,6 +55,7 @@ public class OfferRemoveLoops {
|
||||
testQueue(new LinkedBlockingDeque());
|
||||
testQueue(new ArrayBlockingQueue(10));
|
||||
testQueue(new PriorityBlockingQueue(10));
|
||||
testQueue(new ConcurrentLinkedDeque());
|
||||
testQueue(new ConcurrentLinkedQueue());
|
||||
testQueue(new LinkedTransferQueue());
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
@ -62,6 +63,7 @@ public class RemovePollRace {
|
||||
|
||||
Collection<Queue<Boolean>> concurrentQueues() {
|
||||
List<Queue<Boolean>> queues = new ArrayList<Queue<Boolean>>();
|
||||
queues.add(new ConcurrentLinkedDeque<Boolean>());
|
||||
queues.add(new ConcurrentLinkedQueue<Boolean>());
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, false));
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, true));
|
||||
|
Loading…
Reference in New Issue
Block a user