This article has participated in the third “topic writing” track of the Denver Creators Training Camp. For details, check out: Digg Project | Creators Training Camp third is ongoing, “write” to make a personal impact.
Already profile
ReentrantLock is a reentrant mutex provided in Java and distributed packages. Lock interface, the same function as synchronized, but more flexible than synchronized, but we need to manually obtain/release the Lock. ReentrantLock and synchronized are similar in basic usage, behavioral semantics, and reentrant.
Already implemented
Already all related operations are lock through the Sync class implements, Sync inheritance in AbstractQueuedSynchronizer synchronous queue, and some common interface. NonfairSync inherits Sync and implements an unfair way to acquire locks. FairSync inherits Sync and implements a fair way to acquire locks.
Some of the methods ReentrantLock provides are as follows:
Int getHoldCount() returns the Thread currently holding the lock, or null protected Thread getOwner() if the lock is not held by any Thread; Protected Collection<Thread> getQueuedThreads(); Int getQueueLength(); int getQueueLength(); Protected Collection<Thread> getWaitingThreads(Condition Condition); protected Collection<Thread> getWaitingThreads(Condition Condition); Int getWaitQueueLength(Condition Condition); // Return the count of threads that did not execute signal() after calling the await method of the current lock resource Condition object. Boolean hasQueuedThread(Thread Thread); Boolean hasQueuedThreads(); Boolean hasWaiters(Condition Condition); // Returns the current lock type, true if it is a fair lock, Boolean isHeldByCurrentThread(); Boolean isLocked(); flase Boolean isFair();Copy the code
The lock process in the source code of unfair lock is as follows:
// Implementation of NonfairSync, This extends to Sync Static Final Class NonfairSync {private static Final Long serialVersionUID = 7316153563782823691L; // Lock interface implementation; // The Owner of the lock will be set if the lock Owner is obtained successfully. Otherwise, acquire is called to acquire the lock, acquire or tryAcquire is called to acquire the lock, and tryAcquire obtains the lock in an unfair way. final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected Final Boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); }}Copy the code
The meaning of this code is:
If the CAS variable State (synchronization State) was successfully set, that is, the lock was successfully acquired, the current thread is set to the exclusive thread.
If the variable State (synchronization State) fails to be set through CAS, that is, the lock fails to be obtained, enter the Acquire method for subsequent processing.
Look at the fair lock source code to get the lock:
// The implementation of FairSync, It extends Sync {private static Final Long serialVersionUID = -3000897897090466540L; // Lock interface implementation, own call acquire lock; // Acquire calls tryAcquire to acquire the lock, and tryAcquire obtains the lock in a fair (FIFO) // manner. final void lock() { acquire(1); } protected final Boolean tryAcquire(int acquires) {final Thread current = thread.currentThread (); Int c = getState(); int c = getState(); // No thread to acquire lock? If (c == 0) {// The current node has no precursor node and the current thread CAS status update succeeded; If (! hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; }} // The thread that acquires the lock is the current thread. Else if (current == getExclusiveOwnerThread()) {int nexTC = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }}Copy the code
SetExclusiveOwnerThread () is used to record the Thread that acquired the exclusive lock when a node acquired the lock. When a thread acquires a lock and the lock is occupied, the system determines whether the thread occupying the lock is the current thread. If yes, the lock status is directly updated, indicating that the lock is obtained. Otherwise, the lock fails to be obtained.
Fair lock is implemented through FairSync. When tryAcquire locks, it will judge whether the current node in the synchronization queue has a precursor node. If there are precursor nodes, the lock fails to be obtained and the synchronization queue is entered to wait for the lock to be obtained. If there is no precursor node, it indicates that the current node is the one that waits the longest in the synchronization queue. In this case, the current node preferentially obtains lock resources.
Non-fair lock is implemented through NonfairSync. During lock and tryAcquire, CAS will try to obtain the lock first, and then enter the synchronization queue to wait until the lock fails. As a result, when a thread has just released the lock and the head node unparked in the synchronization queue has not acquired the lock in the CAS interval, the current thread obtains the lock through CAS before the head node of the synchronization queue. It is unfair to make some threads wait too long to acquire the lock.
Unlock () in ReetrantLock releases the lock
When using an explicit lock such as ReetrantLock, you also need to manually release the lock resource after acquiring the lock. Unlock () releases the lock as follows:
// ReetrantLock → unlock() public void unlock() {sync.release(1); } // AQS → release() public final Boolean release(int arg) {if (tryRelease(arg)) head; if (h ! = null && h.waitStatus ! If (0) // Wake up the unparkprecursor (h); return true; } return false; } // ReentrantLock → Sync → tryRelease(int Releases) protected final Boolean tryRelease(int Releases) { Get lock +, release lock -int c = getState() -releases lock; If (thread.currentThread ()!) if (thread.currentThread ()! = getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; If (c == 0) {free = true; // Set Owner to null setExclusiveOwnerThread(null); } // Set the update synchronization state setState(c); return free; }Copy the code
Unlock () releases the lock by calling tryRelease(int Releases), and tryRelease(int Releases) is implemented by ReetrantLock, because there is no implementation in AQS, The release of lock resources wakes up the threads of the succeeding nodes using the unparkprecursor (h). The code for the unparkprecursor (H) is as follows:
Private void unparksucceeded (Node Node) {// Node is the Node of the current thread, the wait state of the current thread int ws = node.waitStatus; CompareAndSetWaitStatus (node, ws, 0); Node s = node.next; Subsequent / / get the current node if (s = = null | | s. aitStatus > 0) {/ / if is empty or has finished s = null; for (Node t = tail; t ! = null && t ! = node; If (t.waitStatus <=0) s = t; if (t.waitStatus <=0) s = t; } if (s ! = null) LockSupport.unpark(s.thread); // Wake up the subsequent node thread}Copy the code
conclusion
ReetrantLock summary. Synchronization status: Displays the possession status of lock resources externally. Synchronization queue: Stores the thread that failed to acquire the lock. Wait queue: Used for multi-conditional wake up. Node Node: Each Node of the queue, thread encapsulation body. Cas changes the synchronization status identifier, fails to obtain the lock, joins the synchronization queue block, and wakes up the first node thread in the synchronization queue when the lock is released.
Lock process: call tryAcquire() to modify identity state, return true on success, join queue on failure. If the node is in signal state, block and suspend the current thread. If not, check whether the status is Cancel. If yes, go through and delete all the nodes in the queue. If the node is in the 0 or propagate state, change it to signal state. If the block is head, the lock is acquired on success, and the block continues on failure.
Unlock process: Call tryRelease() to change identity state, return true on success, false on failure. After the lock is released, the thread node that wakes up after the synchronization queue will automatically replace the current node as the head node.