Function is introduced
ReentrantLock, a concrete implementation class of a reentrant mutex that has the same basic behavior and semantics as synchronized locks, but with extended capabilities. The ReentrantLock lock is owned by the thread that was last successfully locked and not released. When the lock does not belong to any thread, the thread that called the lock will return and successfully acquire the lock. This method returns immediately if the current thread already owns the lock.
Simply put, the synchronized keyword has the same function as the synchronized keyword and can lock shared resources to ensure access security.
How to use
class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock(); }}Copy the code
Lock. Lock () is used to start a lock, followed by statements that need to be synchronized until lock.unlock() is completed. Unlike synchronized, ReentrantLock requires manual release. Otherwise a deadlock will occur
Source code analysis
Let’s go straight to source analysis from the sample code
public ReentrantLock() {
sync = new NonfairSync();
}
Copy the code
The first step is to create an instance object of ReentrantLock directly using the no-argument constructor. By default, the no-argument constructor uses an instance object of NonfairSync to assign the member variable sync
Let’s take a closer look at the structure of the ReentrantLock class and the inner class structure of its unfair and fair locks
The internal structure
private final Sync sync;
Copy the code
abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock(); // Abstract lock method, Final Boolean nonfairTryAcquire(int acquires) {final Thread current = thread.currentThread (); Int c = getState(); If (c == 0) {// The lock is not held by any thread, If (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); Return true; } else if (current == getExclusiveOwnerThread()) {int nexTC = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } // Release lock method protected final Boolean tryRelease(int releases) {int c = getState() -releases; if (Thread.currentThread() ! = getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }}Copy the code
The first is to hold the abstract internal class Sync that inherits from AQS. From the understanding of the previous chapters, we know that AQS mainly does the maintenance related to the synchronization queue that acquires locks
Abstract lock provisioning is defined in the Sync class, which provides methods for obtaining unfair locks and releasing locks
The acquisition code of unfair lock is relatively simple, mainly to determine whether the current lock has been held by other threads, as shown in the following figure
It should be noted that here we can understand the principle of reentrancy:
When a thread acquires the lock, it acquires the lock again if the thread that has already acquired the lock is the current threadCopy the code
If the value is not 0, it indicates that the lock has been reentrant for many times. Only after all the reentrant locks are released can the lock be truly released
Sync’s child implementation classes include fair and unfair locking, and how they implement fairness
Static final class NonfairSync extends Sync {final void lock() {if (compareAndSetState(0, 0); 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }}Copy the code
NonFairSync implements the specific semantics of lock, and calls the acquireQueued method of AQS when preempting the lock. The acquireQueued method of AQS is also used in this chapter
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
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;
}
}
Copy the code
public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; // True return h! = t && ((s = h.next) == null || s.thread ! = Thread.currentThread()); }Copy the code
FairSync mainly implements the semantics of lock, and calls the tryAcquire method in the acquire method. The tryAcquire method is basically the same as the unjust lock acquisition method, the only difference being the HasqueuedToraise method, Determine whether the current node has a front node, which is not the head node. If so, it will enter the queue for queuing; if not, it will acquire the lock
conclusion
Today just shows down already the fairness and fair lock lock the exclusive lock acquisition, after we understand the principle of AQS to see this is very easy, as for the interruptible acquiring a lock and lock timeout gets everyone can own, according to the source code for learning in the understanding of the section in the previous chapter AQS interrupt lock, timeout after acquiring a lock of the source code, I think this section will be very easy to read
This article is written by reading the source code and their own understanding, if there is wrong, please point out, thank you