table of Contents
1. ReentrantLock initialization
2. ReentrantLock internal structure
3. Common methods of ReentrantLock
4.1 Principle of lock () method
4.2. Principle of TryAcquire () method
5.1 Principle of lock () and TryAcquire () methods
5.2 The principle of hasQueuedPredecessors () method
6.2 tryLock(long timeout, TimeUnit unit)
ReentrantLock is reentrant the exclusive lock . According to the parameters, it can be determined whether it is a fair lock or an unfair lock .
1. ReentrantLock initialization
// 非公平锁
ReentrantLock lock = new ReentrantLock();
// 公平锁
ReentrantLock lock = new ReentrantLock(true);
ReentrantLock defaults to an unfair lock, and you can pass in the parameter true to obtain a fair lock. The subclasses FairSync and NonfairSync implement lock fair and unfair strategies respectively. ReentrantLock initialization source code is as follows:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2. ReentrantLock internal structure
- ReentrantLock implements the Lock interface
- Internal subclass Sync inherits AbstractQueuedSynchronizer abstract class
- The internal subclasses FairSync and NonfairSync inherit the Sync class
3. Common methods of ReentrantLock
- lock () lock method
- Execution logic: lock ()-> acquire ()-> tryAcquire ()
- unlock () unlock method
- tryLock (), tryLock (long timeout, TimeUnit unit) try to acquire the lock
4. Nonfair lock
4.1 Principle of lock () method
i. Set state from 0 to 1 based on CAS
ii. If the setting is successful, set the exclusive lock thread as the current thread
iii. After the request fails, try again
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 基于 CAS 尝试将 state 从 0 设置为 1
if (compareAndSetState(0, 1))
// 独享锁的线程设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 重试一次(内部调用 tryAcquire 方法)
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
The acquire method in AQS executed after acquiring the lock fails as follows.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
Since AQS does not provide the available tryAcquire method, the tryAcquire method needs to be customized by the subclass, so here tryAcquire will call the tryAcquire method implemented in NonfairSync.
4.2. Principle of TryAcquire () method
The nonfairTryAcquire method is called in the tryAcquire method of NonfairSync
The source code of nonfairTryAcquire is as follows:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 判断 state 是否为 0
if (c == 0) {
// 尝试 state 的值从 0 改为 1
if (compareAndSetState(0, acquires)) {
// 设置获取锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 如果持有锁的线程为当前线程
// state 的值 + acquire
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置 state 值为新值
setState(nextc);
return true;
}
return false;
}
i. Determine if state is 0, if it is 0, try to acquire the lock
ii. Not 0, determine whether the thread holding the lock is the current thread
iii. If it is the current thread, reassign state + acquire to state (so ReentrantLock is a reentrant lock)
iv. The thread holding the lock is not the current thread, false is returned
5. fair lock FairSync
5.1 Principle of lock () and TryAcquire () methods
The principles of fair locks and unfair locks are similar, the difference is that the tryAcquire method has more hasQueuedPredecessors judgment:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 公平锁比非公平锁多了 hasQueuedPredecessors 的判断
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;
}
}
5.2 The principle of hasQueuedPredecessors () method
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
(
(s = h.next) == null ||
s.thread != Thread.currentThread()
);
}
i. If the head node and the tail node are not equal (h! = t), it means that more than one thread is requesting the lock
ii. Determine whether head.next is null, if it is null, return true
h! = t at the same time as h.next == null: when other threads are enqueuing for the first time, compareAndSetHead (node) is completed, and tail = head statement has not been executed, then tail = null, head = newNode , head.next = null
iii. s.thread! = Thread.currentThread () to determine whether the current thread is the second thread
The judgment of hasQueuedPredecessors guarantees the sequence of threads acquiring locks, so FairSync implements fair locks.
6. tryLock method
6.1 tryLock()
Attempt to acquire the lock, true if the lock is acquired, false if the acquisition fails
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
The nonfairTryAcquire called internally by tryLock is consistent with the unfair lock code, so tryLock () uses an unfair strategy.
6.2 tryLock(long timeout, TimeUnit unit)
The difference with tryLock (): This method can set the timeout time, and returns false if the lock is not obtained within the timeout time.
7. Use of ReentrantLock
lock.lock();
try {
//TODO
} finally {
lock.unlock();
}
8. Summary
i. The underlying layer of ReentrantLock is a reentrant exclusive lock implemented using AQS
ii. ReentrantLock has both fair and unfair implementation, the default is unfair
iii. For the state in AQS, lock state + 1, unlock state-1, so you need to unlock a few times
iv. state is 0 means the current lock is idle, greater than or equal to 1, indicating that the lock has been occupied