The source code of this article is based on JDK13
ConcurrentLinkedDeque
Official annotation translation
An unbounded, concurrent, double ended queue is implemented using a linked list. Concurrent write, remove, and access operations between multiple threads can ensure security. When many threads share a common set, ConcurrentLinkedDeque is a good choice. Like other concurrent sets, this class does not accept null elements
Iterators are weakly consistent
It should be noted that, unlike most other collections, the size method is not a constant time operation. Because of the asynchronous nature of the queue, counting the current elements requires traversing all elements. Therefore, if other threads are changing, the size method may return inaccurate numbers
Batch operations do not guarantee atomicity, such as addAll. When foreach and addAll run together, foreach may only observe some elements
This class and its Iterator implement all the optional methods of Queue and Iterator
Source code
definition
public class ConcurrentLinkedDeque<E> extends AbstractCollection<E> implements Deque<E>, java.io.Serializable {
A two - terminal queue
Internal linked list node
static final class Node<E> { volatile Node<E> prev; volatile E item; volatile Node<E> next; }
The pointer of the front and back nodes and the element of the current node
attribute
private transient volatile Node<E> head; private transient volatile Node<E> tail;
Saved head and tail nodes
Construction method
public ConcurrentLinkedDeque() { head = tail = new Node<E>(); } public ConcurrentLinkedDeque(Collection<? extends E> c) { // Copy c into a private chain of Nodes Node<E> h = null, t = null; for (E e : c) { Node<E> newNode = newNode(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { NEXT.set(t, newNode); PREV.set(newNode, t); t = newNode; } } initHeadTail(h, t); }
Two construction methods, one to construct an empty queue and one to initialize a given set into the queue
Team entry method
public void addFirst(E e) { linkFirst(e); } public void addLast(E e) { linkLast(e); } public boolean offerFirst(E e) { linkFirst(e); return true; } public boolean offerLast(E e) { linkLast(e); return true; }
Support the addition of queue head and tail, specifically calling linkFirst and linkLast
- linkFirst
private void linkFirst(E e) { // Create current node final Node<E> newNode = newNode(Objects.requireNonNull(e)); restartFromHead: for (;;) for (Node<E> h = head, p = h, q;;) { // If the front node of the node is not empty, update the p node if ((q = p.prev) != null && (q = (p = q).prev) != null) // Check for head updates every other hop. // If p == q, we are sure to follow head instead. p = (h != (h = head)) ? h : q; // Node p is out of the queue and starts from scratch else if (p.next == p) // PREV_TERMINATOR continue restartFromHead; else { // p is first node // Sets the current node as the first NEXT.set(newNode, p); // CAS piggyback // cas updates the related attributes, the pre attributes of the original header node, and the new header node if (PREV.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". if (p != h) // hop two nodes at a time; failure is OK HEAD.weakCompareAndSet(this, h, newNode); return; } // Lost CAS race to another thread; re-read prev } } }
Set the current node as the first node, which is realized by CAS + spin. When it is found that the existing head node is out of the queue, find the head node again
- linkLast
The link is a node, which is consistent with the head node
private void linkLast(E e) { final Node<E> newNode = newNode(Objects.requireNonNull(e)); restartFromTail: for (;;) for (Node<E> t = tail, p = t, q;;) { if ((q = p.next) != null && (q = (p = q).next) != null) // Check for tail updates every other hop. // If p == q, we are sure to follow tail instead. p = (t != (t = tail)) ? t : q; else if (p.prev == p) // NEXT_TERMINATOR continue restartFromTail; else { // p is last node PREV.set(newNode, p); // CAS piggyback if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". if (p != t) // hop two nodes at a time; failure is OK TAIL.weakCompareAndSet(this, t, newNode); return; } // Lost CAS race to another thread; re-read next } } }
Out of line operation
public E pollFirst() { restart: for (;;) { for (Node<E> first = first(), p = first;;) { // Queue head node, cas change attribute final E item; if ((item = p.item) != null) { // recheck for linearizability if (first.prev != null) continue restart; if (ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } } // Out of line, start again if (p == (p = p.next)) continue restart; // p is empty, the queue is empty, and null is returned if (p == null) { if (first.prev != null) continue restart; return null; } } } } public E pollLast() { restart: for (;;) { for (Node<E> last = last(), p = last;;) { final E item; if ((item = p.item) != null) { // recheck for linearizability if (last.next != null) continue restart; if (ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } } if (p == (p = p.prev)) continue restart; if (p == null) { if (last.next != null) continue restart; return null; } } } }
Corresponding to joining the team, pop up the head or tail of the team. The idea is the same
Normal queue operation
Double ended queues can provide queue in and queue out operations like ordinary queues. At this time, they are a FIFO queue, that is, the queue in is added to the tail of the queue, and the queue out obtains elements from the head of the queue
summary
Consistent with the idea of concurrent linked queue, it is implemented by CAS + spin. It only provides the method related to double ended queue
Reference articles
End.
Contact me
Finally, welcome to my personal official account, Yan Yan ten, which will update many learning notes from the backend engineers. I also welcome direct official account or personal mail or email to contact me.
The above are all personal thoughts. If there are any mistakes, please correct them in the comment area.
Welcome to reprint, please sign and keep the original link.
Contact email: huyanshi2580@gmail.com
For more study notes, see personal blog or WeChat official account, Yan Yan ten > > Huyan ten