ReentrantLock
class belongs java.util.concurrent
to. It implements Lock, java.io.Serializable
two interfaces and is a reentrant mutex. The so-called reentrancy is that a thread can repeatedly acquire the lock it already holds.
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
abstract static class Sync extends AbstractQueuedSynchronizer
ReentrantLock
The mechanism for implementing locks is through Sync
operations. Sync
Classes inherit from AbstractQueuedSynchronizer
classes. This also shows that it ReentrantLock
is based on AQS
the implementation. '
Sync
, and both are static inner classes. and are specific implementation classes, which correspond to fair locks and unfair locks respectively. Fairness mainly refers to acquiring locks in the order of principles, and unfairness can choose to acquire locks according to the defined rules.FairSync
NonFairSync
ReentrantLock
FairSync
NonFairSync
Sync
FIFO
NonFairSync source code analysis
Unfair locking NonFairSync
is the ReentrantLock
default implementation. Here you can take a look at its lock
implementation process:
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- First, by
CAS
updatingstate
the state, if the update is successful, the lock is acquired, and the current thread is set as the owner of the lock.
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
- If the update fails, it indicates that the current lock is held by another thread. method will be called
acquire(1)
.acquire
The specific implementation is as follows:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
tryAcquire
process, will try to acquire the lock again, which is overridden tryAcquire
in the static inner class class, the concrete implementation is the method:NonfairSync
Sync
nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
Its main process is to get state
the value first, and if it is equal to 0, then pass the CAS
updated state
value. If it state
is not 0, it is judged whether the current thread is the holder of the lock, if it is, it will state
add 1 and return true
.
If it tryAcquire
still fails, it will be called first to addWaiter(Node.EXCLUSIVE)
add the current thread to the tail of the waiting queue. Then the method will be called acquireQueued
, acquireQueued
which is mainly used to block the thread:
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
This is a cyclic spin operation. Before blocking the thread, first determine whether the head
node in front of the thread itself is a node. If so, re-acquire the lock. After the acquisition is successful, return and cancel the continuous acquisition process. If not, call the shouldParkAfterFailedAcquire
method to judge whether the current thread should be blocked, mainly through the node waitStatus
to judge.
FairSync source code analysis
The implementations of fair locks FairSync
and unfair locks NonFairSync
are very similar. Let's compare the differences between the two.
FairSync
In thelock
method, there is no way to obtain the lockNonFairSync
through theCAS
operation firststate
, buttryAcquire
to obtain the lock directly.
final void lock() {
acquire(1);
}
FairSync
In the process of acquiring the lock, the versiontryAcquire
needs to first determine whether there are other waiting threads in the queue, and if not, go back and try to acquire the lock.
/**
* 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) {
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;
}
unlock() releases the lock
Release locks do not distinguish between fair and unfair. The main job is to reduce state
the value. When state
waiting for 0, release the lock and wake up other threads in the team to acquire the lock.
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
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;
}
Summarize
ReentrantLock
It isAQS
astate
field used to determine whether it is occupied.公平
The非公平
difference is whether the way to acquire locks is in order.state
Actions areCAS
implemented through. A queue that is blocked due to a preemptive lock is implemented through a queue.- In the process of blocking the thread,
AQS
there is a process of spinning, not directly blocking if the lock is not obtained.