Teacher Dafei takes you to see under Threads (Concurrent Container-SynchronousQueue)

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.

Posted by alwaysme on Fri, 10 May 2019 03:32:38 -0700