您好,登錄后才能下訂單哦!
這篇“java底層組合AQS實現類kReentrantLock源碼分析”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“java底層組合AQS實現類kReentrantLock源碼分析”文章吧。
ReentrantLock 中文我們習慣叫做可重入互斥鎖,可重入的意思是同一個線程可以對同一個共享資源重復的加鎖或釋放鎖,互斥就是 AQS 中的排它鎖的意思,只允許一個線程獲得鎖。
我們來一起來看下類注釋上都有哪些重要信息:
可重入互斥鎖,和 synchronized 鎖具有同樣的功能語義,但更有擴展性;構造器接受 fairness 的參數,fairness 是 ture 時,保證獲得鎖時的順序,false 不保證;公平鎖的吞吐量較低,獲得鎖的公平性不能代表線程調度的公平性;tryLock() 無參方法沒有遵循公平性,是非公平的(lock 和 unlock 都有公平和非公平,而 tryLock 只有公平鎖,所以單獨拿出來說一說)。我們補充一下第二點,ReentrantLock 的公平和非公平,是針對獲得鎖來說的,如果是公平的,可以保證同步隊列中的線程從頭到尾的順序依次獲得鎖,非公平的就無法保證,在釋放鎖的過程中,我們是沒有公平和非公平的說法的。
ReentrantLock 類本身是不繼承 AQS 的,實現了 Lock 接口,如下:
public class ReentrantLock implements Lock, java.io.Serializable {}
Lock 接口定義了各種加鎖,釋放鎖的方法,接口有如下幾個:
// 獲得鎖方法,獲取不到鎖的線程會到同步隊列中阻塞排隊void lock();// 獲取可中斷的鎖void lockInterruptibly() throws InterruptedException;// 嘗試獲得鎖,如果鎖空閑,立馬返回 true,否則返回 falseboolean tryLock();// 帶有超時等待時間的鎖,如果超時時間到了,仍然沒有獲得鎖,返回 falseboolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 釋放鎖void unlock();// 得到新的 ConditionCondition newCondition();
ReentrantLock 就負責實現這些接口,我們使用時,直接面對的也是這些方法,這些方法的底層實現都是交給 Sync 內部類去實現的,Sync 類的定義如下:
abstract static class Sync extends AbstractQueuedSynchronizer {}
Sync 繼承了 AbstractQueuedSynchronizer ,所以 Sync 就具有了鎖的框架,根據 AQS 的框架,Sync 只需要實現 AQS 預留的幾個方法即可,但 Sync 也只是實現了部分方法,還有一些交給子類 NonfairSync 和 FairSync 去實現了,NonfairSync 是非公平鎖,FairSync 是公平鎖,定義如下:
// 同步器 Sync 的兩個子類鎖static final class FairSync extends Sync {}static final class NonfairSync extends Sync {}
幾個類整體的結構如下:
圖中 Sync、NonfairSync、FairSync 都是靜態內部類的方式實現的,這個也符合 AQS 框架定義的實現標準。
3、構造器ReentrantLock 構造器有兩種,代碼如下:
// 無參數構造器,相當于 ReentrantLock(false),默認是非公平的public ReentrantLock() { sync = new NonfairSync();} public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();}
無參構造器默認構造是非公平的鎖,有參構造器可以選擇。
從構造器中可以看出,公平鎖是依靠 FairSync 實現的,非公平鎖是依靠 NonfairSync 實現的。
Sync 表示同步器,繼承了 AQS,UML 圖如下:
從 UML 圖中可以看出,lock 方法是個抽象方法,留給 FairSync 和 NonfairSync 兩個子類去實現,我們一起來看下剩余重要的幾個方法。
4.1、nonfairTryAcquire// 嘗試獲得非公平鎖final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 同步器的狀態是 0,表示同步器的鎖沒有人持有 if (c == 0) { // 當前線程持有鎖 if (compareAndSetState(0, acquires)) { // 標記當前持有鎖的線程是誰 setExclusiveOwnerThread(current); return true; } } // 如果當前線程已經持有鎖了,同一個線程可以對同一個資源重復加鎖,代碼實現的是可重入鎖 else if (current == getExclusiveOwnerThread()) { // 當前線程持有鎖的數量 + acquires int nextc = c + acquires; // int 是有最大值的,<0 表示持有鎖的數量超過了 int 的最大值 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } //否則線程進入同步隊列 return false;}
以上代碼有三點需要注意:
通過判斷 AQS 的 state 的狀態來決定是否可以獲得鎖,0 表示鎖是空閑的;else if 的代碼體現了可重入加鎖,同一個線程對共享資源重入加鎖,底層實現就是把 state + 1,并且可重入的次數是有限制的,為 Integer 的最大值;這個方法是非公平的,所以只有非公平鎖才會用到,公平鎖是另外的實現。無參的 tryLock 方法調用的就是此方法,tryLock 的方法源碼如下:
public boolean tryLock() { // 入參數是 1 表示嘗試獲得一次鎖 return sync.nonfairTryAcquire(1);}
// 釋放鎖方法,非公平和公平鎖都使用protected final boolean tryRelease(int releases) { // 當前同步器的狀態減去釋放的個數,releases 一般為 1 int c = getState() - releases; // 當前線程根本都不持有鎖,報錯 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 如果 c 為 0,表示當前線程持有的鎖都釋放了 if (c == 0) { free = true; setExclusiveOwnerThread(null); } // 如果 c 不為 0,那么就是可重入鎖,并且鎖沒有釋放完,用 state 減去 releases 即可,無需做其他操作 setState(c); return free;}
tryRelease 方法是公平鎖和非公平鎖都公用的,在鎖釋放的時候,是沒有公平和非公平的說法的。
從代碼中可以看到,鎖最終被釋放的標椎是 state 的狀態為 0,在重入加鎖的情況下,需要重入解鎖相應的次數后,才能最終把鎖釋放,比如線程 A 對共享資源 B 重入加鎖 5 次,那么釋放鎖的話,也需要釋放 5 次之后,才算真正的釋放該共享資源了。
FairSync 公平鎖只實現了 lock 和 tryAcquire 兩個方法,lock 方法非常簡單,如下:
// acquire 是 AQS 的方法,表示先嘗試獲得鎖,失敗之后進入同步隊列阻塞等待final void lock() { acquire(1);}
tryAcquire 方法是 AQS 在 acquire 方法中留給子類實現的抽象方法,FairSync 中實現的源碼如下:
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // hasQueuedPredecessors 是實現公平的關鍵 // 會判斷當前線程是不是屬于同步隊列的頭節點的下一個節點(頭節點是釋放鎖的節點) // 如果是(返回false),符合先進先出的原則,可以獲得鎖 // 如果不是(返回true),則繼續等待 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;}
代碼和 Sync 的 nonfairTryAcquire 方法實現類似,唯一不同的是在獲得鎖時使用 hasQueuedPredecessors 方法體現了其公平性。
NonfairSync 底層實現了 lock 和 tryAcquire 兩個方法,如下:
// 加鎖final void lock() { // cas 給 state 賦值 if (compareAndSetState(0, 1)) // cas 賦值成功,代表拿到當前鎖,記錄拿到鎖的線程 setExclusiveOwnerThread(Thread.currentThread()); else // acquire 是抽象類AQS的方法, // 會再次嘗試獲得鎖,失敗會進入到同步隊列中 acquire(1);}// 直接使用的是 Sync.nonfairTryAcquire 方法 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires);}
以上內容主要說了 ReentrantLock 的基本結構,比較零散,那么這些零散的結構如何串聯起來呢?我們是通過 lock、tryLock、unlock 這三個 API 將以上幾個類串聯起來,我們來一一看下。
lock 的代碼實現:
public void lock() { sync.lock();}
其底層的調用關系(只是簡單表明調用關系,并不是完整分支圖)如下:
7.2 tryLock 嘗試加鎖tryLock 有兩個方法,一種是無參的,一種提供了超時時間的入參,兩種內部是不同的實現機制,代碼分別如下:
// 無參構造器public boolean tryLock() { return sync.nonfairTryAcquire(1);}// timeout 為超時的時間,在時間內,仍沒有得到鎖,會返回 falsepublic boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout));}
接著我們一起看下兩個 tryLock 的調用關系圖,下圖顯示的是無參 tryLock 的調用關系圖,如下:
我們需要注意的是 tryLock 無參方法底層走的就是非公平鎖實現,沒有公平鎖的實現。
下圖展示的是帶有超時時間的有參 tryLock 的調用實現圖:
7.3、unlock 釋放鎖unlock 釋放鎖的方法,底層調用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成兩步:
嘗試釋放鎖,如果釋放失敗,直接返回 false;釋放成功,從同步隊列的頭節點的下一個節點開始喚醒,讓其去競爭鎖。第一步就是我們上文中 Sync 的 tryRelease 方法(4.1),第二步 AQS 已經實現了。
unLock 的源碼如下:
// 釋放鎖public void unlock() { sync.release(1);}
ReentrantLock 對 Condition 并沒有改造,直接使用 AQS 的 ConditionObject 即可。
以上就是關于“java底層組合AQS實現類kReentrantLock源碼分析”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。