Author: Wang Yifei, Senior Instructor of Percussion Wolf.Original article, please indicate the source for reprinting.
Continue with this article on SynchronousQueue queue unfair policy put and take operations
Source Code Analysis
2:Unfair Lock Policy - put / take
public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); if (transferer.transfer(e, false, 0) == null) { Thread.interrupted(); throw new InterruptedException(); } }
public E take() throws InterruptedException { E e = transferer.transfer(null, false, 0); if (e != null) return e; Thread.interrupted(); throw new InterruptedException(); }
Both put and take methods are called:
Call transfer method:
put : transferer.transfer(e, false, 0)
take: transferer.transfer(null, false, 0);
From the previous internal structure, the underlying SynchronousQueue unfair policy is actually delegated to the TransferStack stack implementation, while the internal storage data uses the SNode stack node to look at the node source code:
static final class SNode { volatile SNode next; //Next Node volatile SNode match; //Matching Nodes volatile Thread waiter; //Thread suspend and wake control: park/unpark Object item; //Node Content //Mode control variable //There are three unfair policy models: //REQUEST: Represents a consumer Data-take operation //DATA: Represents a production data-put operation //FULFILLING: Between the last two states //There is one more state reason: TransferStack pairing works by adding to the top of the stack each thread pairs, whether take or put. If you find that a stacked thread can pair with an in-stack thread at this time, the thread can use the FULFILLING state class flag: the pairing logic thread will be executed, the other pairing threads will be prompted to skip this section when pairingPoint, match other threads. int mode; //cas atomic operation, set next node boolean casNext(SNode cmp, SNode val) {...} //Node matches, match succeeds, unpark waits for thread boolean tryMatch(SNode s) {...} //Cancel the node and set its content to itself void tryCancel() {...} //Determine if the operation is over boolean isCancelled() {...} }
The fair lock policy is studied here, so the transfer variable holds an instance of the TransferStack class
E transfer(E e, boolean timed, long nanos) { SNode s = null; //Determine the current mode of operation based on transfer method parameter e //Valued DATA Conversely REQUEST int mode = (e == null) ? REQUEST : DATA; for (;;) { SNode h = head; //Initially, the head er is null //The first put or take h==null holds, if a suspended thread already exists, the stack can only enter if take or put mode must be consistent. This branch is the stack branch and will not be paired if (h == null || h.mode == mode) { if (timed && nanos <= 0) { //Timeout operation //There is a pairing thread in the queue, but the pairing thread was canceled if (h != null && h.isCancelled()) casHead(h, h.next); //Move down the top node of the stack and out else return null; //Satisfy and enter this branch, create a new node, the current thread becomes the top node on the stack } else if (casHead(h, s = snode(s, e, h, mode))) { //Once the top position of the stack is moved successfully, suspend the current thread and wait for the pairing thread SNode m = awaitFulfill(s, timed, nanos); if (m == s) { clean(s); //Once the pairing succeeds, the corresponding thread goes out of the stack (cleared) return null; } if ((h = head) != null && h.next == s) casHead(h, s.next); return (E) ((mode == REQUEST) ? m.item : s.item); //Return results } //When a different mode operation occurs, then a pairing is required, which satisfies that a single-Front top stack node is not a pairing node, so other competing threads that attempt to pair the top stack node are excluded } else if (!isFulfilling(h.mode)) { if (h.isCancelled()) //Check that if the top node of the stack pairs successfully with other threads, the top node of the stack moves down casHead(h, h.next); //If there are no competing threads, the threads are stacked as top nodes and paired else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { for (;;) { //Get target paired nodes SNode m = s.next; //If the target pairing node is null, it means that the target pairing node has been grabbed by another thread. if (m == null) { casHead(s, null); //null from top node of new move stack s = null; //Pairing fails, jump out of the loop, judge from the new cycle break; } //If not null, get the next node of the target paired node SNode mn = m.next; if (m.tryMatch(s)) { //Try pairing casHead(s, mn); //The pairing succeeds, moving the top node of the stack to mn, and two nodes of the pairing leave the stack at the same time, returning the result return (E) ((mode == REQUEST) ? m.item : s.item); } else //If the pairing fails, find the next node of the original target node and recycle the pairing s.casNext(m, mn); } } } else { //Entering this branch means that the target pairing node is a pairing node //Skip directly, pair the next node, and the next logic is the same as the above pairing SNode m = h.next; if (m == null) casHead(h, null); else { SNode mn = m.next; if (m.tryMatch(h)) casHead(h, mn); else h.casNext(m, mn); } } } }
Unfair Policy Operational Flow Chart:
It is important to note that if there are too many elements and concurrently, the threads of the pairing will pair exactly who, then it will be random and will not match in the desired order as shown in the diagram.That's why it's an unfair strategy.
Summary:
Combined code: The transferer execution process can be summarized as follows:
1: transferer calls transfer method to implement take and put operation of SynchronousQueue fair queue
2: Whether it is a take or put thread, as long as it is in the same mode as the top node of the stack, it goes on the stack and spins to find a match, and no pairing is found to hang.If time is exhausted or cancelled, list directly.
3:If a paired node is found, mark the current thread FULFILLING to try pairing.If there is a competing pairing thread at this time, check that the node has a FULFILLING tag and skip directly to find the next pairing node.
4:If the pairing succeeds, eject the current two successful pairing nodes.If the pairing fails, loop from the beginning.