When we read the source code of ReentrantLock, we found the following three classes:
Among them: There are three classes: Sync, NonfairSync, and FairSync inside the ReentrantLock class. The NonfairSync and FairSync classes inherit from the Sync class, and the Sync class inherits from the AbstractQueuedSynchronizer abstract class. Of course, the ReentrantLock class itself inherits the Lock interface.
Lock
The lock interface defines five methods commonly used in concurrency:
public interface Lock { // Get the lock void lock(); // Obtaining the lock can be interrupted, that is, interrupt can be interrupted during the lock acquisition process. The difference is that synchronized locks cannot be interrupted. void lockInterruptibly() throws InterruptedException; //Try to acquire the lock. The lock can only be acquired when the lock is idle (it will not wait if the lock is not acquired) boolean tryLock(); //Try to acquire the lock within a given time, returning true if successful and false if failed. boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // release lock void unlock(); //Waiting and wake-up mechanism Condition newCondition(); }
ReentrantLock inherits the lock interface, and these methods will also be rewritten inside ReentrantLock.
Sync
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; // Get the lock abstract void lock(); //Acquire unfair lock final boolean nonfairTryAcquire(int acquires) { // Get the current thread final Thread current = Thread.currentThread(); // Get status int c = getState(); if (c == 0) { //Indicates that no thread is competing for the lock //Try to get the lock through CAS, status 0 means the lock is not occupied!!! if (compareAndSetState(0, acquires)) { //Set the current thread exclusive, that is, set the current thread exclusive lock setExclusiveOwnerThread(current); return true; // success } } //If it is locked, further determine that the current thread owns the lock (that is, reentrant lock) else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; // Increase the number of reentries if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //Set status setState(nextc); // success return true; } // fail return false; } //Try to release resources protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) // The current thread is not an exclusive thread throw new IllegalMonitorStateException(); // Release flag boolean free = false; if (c == 0) { free = true; // Already released, clear exclusive use setExclusiveOwnerThread(null); } //Set the flag setState(c); return free; } // Determine whether the resource is occupied by the current thread protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); } // Create a new condition final ConditionObject newCondition() { return new ConditionObject(); } // Get the thread holding the lock final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } //return status final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } //Whether it is locked final boolean isLocked() { return getState() != 0; } /** * Reconstitutes the instance from a stream (that is, deserializes it). */ // Custom deserialization logic private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
Sync is an abstract class, so there must be classes that inherit it. There are two Sync implementations in ReentrantLock, namely NonfairSync and FairSync.
NonfairSync and FairSync
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { // Use CAS to lock (if state is equal to 0, set it to 1 and return true, otherwise return false), if (compareAndSetState(0, 1)) // If the lock is successful, set the exclusive thread as the current thread setExclusiveOwnerThread(Thread.currentThread()); else // If the lock fails, call the acquire method of the AbstractQueuedSynchronizer class. acquire(1); } protected final boolean tryAcquire(int acquires) { // Call the nonfairTryAcquire method of the parent class Sync return nonfairTryAcquire(acquires); } }
We can see that in unfair locks, if the current thread lock occupancy status is 0, CAS will be directly used to try to acquire the lock. There is no need to join the queue, and then wait for the queue head thread to wake up and then acquire the lock, so the efficiency is relatively high. It will be faster than fair lock. ReentrantLock is an unfair lock by default
Let’s look at the source code of FairSync:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//unlocked state //First determine that there is no waiting node, then open CAS to get the lock if (!hasQueuedPredecessors() & amp; & amp; compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
We can see that in fair lock, if the current thread lock occupancy status is 0, it will first synchronize whether there is a waiting thread in the queue. If not, it will perform the lock operation, thus following the FIFO principle. First come, first served. Therefore, the efficiency is slower than unfair lock. Within ReentrantLock, both the NonfairSync, FairSync, and Sync classes ultimately inherit AbstractQueuedSynchronizer. This is also a very important part. Let’s take a look at it together.