ReentrantLock介紹
ReentrantLock也是在JUC併發包下的,實現可重入鎖的機制。也是在AQS基礎之上的,在上一篇AQS的講解基礎下,理解這篇會比較的簡單。知識圖譜整理之Java基礎AbstractQueuedSynchronizer
。
ReentrantLock的概覽
要了解這個類,主要還是從它的使用方法上入手,因爲這個是的數據結構基礎是在AQS之上的,所以內部不再重寫數據結構,主要關注點還是在於這個類的方法。
在這個講輸出的年代,ReentrantLock是如何實現輸出的呢?他其實是內部維護了一個繼承AQS的類,然後在通過獲取鎖是否公平的方式分爲了兩種策略,即公平鎖和非公平鎖。首先我們來看下這個基礎類Sync源碼。
源碼解讀
Sync內部類源碼
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}
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;
}
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();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
- 首先可以看到這個類是個抽象類,所以可以確定它肯定有子類實現。
- nonfairTryAcquire方法比較關鍵,是非公平嘗試獲取鎖,裏面的邏輯其實就比較簡單了,無非是先獲取state變量的值,通過CAS方式修改,如果修改成功就代表獲取到了鎖標識。如果失敗,會判斷當前線程是否爲已獲取到鎖標識的線程,如果是則state+1來代表重入次數。
- tryRelease的邏輯就不多說了,就是關於state變量操作
- 這個類主要是這兩個方法比較重要,其他的等我們用到了在做深入
NonfairSync內部類源碼
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() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
- 這裏就實現了Sync的lock方法,也比較的簡單
- 如果獲取鎖不成功,我們看到調用了acquire,這是AQS的方法,還記得這裏的操作麼?不記得可以看看AQS的,大概就是調用了tryAcquire方法,如果不行就放進同步等待隊列。
- tryAcquire就是調用了Sync的nonfairTryAcquire方法,這樣整體就串起來了
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) {
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;
}
}
- 首先這個是公平鎖,何爲公平鎖,就是先進來的先獲取到鎖。基於這個條件我們來看下是如何實現的
- 首先是lock方法,很簡單就是調用了AQS的acquire,在調用FairSync實現的tryAcquire方法
- tryAcquire是公平鎖的關鍵,就是在state爲0時,即可以爭取鎖時,哪些線程可以爭。
- hasQueuedPredecessors方法很關鍵,這個方法作用是查詢是否有任何線程在等待獲取比當前線程更長的。
hasQueuedPredecessors方法源碼
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
- 第一個條件 h != t代表如果當前沒有任何其他競爭線程在隊列中的,直接返回false。h==t代表沒有其他在等待的競爭線程
- ((s = h.next) == null || s.thread != Thread.currentThread())這個條件判斷是否頭結點的下一個節點的線程是否是當前線程。這裏要結合這個方法的上層,是判斷如果非則繼續執行,所以整體邏輯就是如果是當前線程,則繼續往下執行。
鎖如何對外輸出
從構造方法開始:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
其實就是用了策略模式,確定鎖策略,實現交給對應策略子類。
今日總結
今天閱讀了關於ReentrantLock的源碼,在理解了AQS的基礎上,閱讀起來比較簡單,再來曬下今天的知識圖譜輸出: