理解ReentrantLock的公平鎖和非公平鎖
點擊關注,與你共同成長!

學習AQS的時候,了解到AQS依賴于內(nèi)部的FIFO同步隊列來完成同步狀態(tài)的管理,當前線程獲取同步狀態(tài)失敗時,同步器會將當前線程以及等待狀態(tài)等信息構(gòu)造成一個Node對象并將其加入到同步隊列,同時會阻塞當前線程,當同步狀態(tài)釋放時,會把首節(jié)點中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。
這時,我有了一個疑問,AQS的同步隊列是FIFO的,就是先來排隊的先走。那怎么實現(xiàn)非公平鎖呢?查閱了一些資料,總算知道了。
首先從公平鎖開始看起。
ReentrantLock 的公平鎖
ReentrantLock 默認采用非公平鎖,除非在構(gòu)造方法中傳入?yún)?shù) true 。
????//默認
????public?ReentrantLock()?{
????????sync?=?new?NonfairSync();
????}
????//傳入true?or?false
????public?ReentrantLock(boolean?fair)?{
????????sync?=?fair???new?FairSync()?:?new?NonfairSync();
????}
公平鎖的 lock 方法:
????static?final?class?FairSync?extends?Sync?{
????????final?void?lock()?{
????????????acquire(1);
????????}
????????//?AbstractQueuedSynchronizer.acquire(int?arg)
????????public?final?void?acquire(int?arg)?{
????????????if?(!tryAcquire(arg)?&&
????????????????acquireQueued(addWaiter(Node.EXCLUSIVE),?arg))
????????????????selfInterrupt();
????????}
????????protected?final?boolean?tryAcquire(int?acquires)?{
????????????final?Thread?current?=?Thread.currentThread();
????????????int?c?=?getState();
????????????if?(c?==?0)?{
????????????????// 1. 和非公平鎖相比,這里多了一個判斷:是否有線程在等待
????????????????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;
????????}
????}
我們可以看到,在注釋1的位置,有個!hasQueuedPredecessors()條件,意思是說當前同步隊列沒有前驅(qū)節(jié)點(也就是沒有線程在等待)時才會去compareAndSetState(0, acquires)使用CAS修改同步狀態(tài)變量。所以就實現(xiàn)了公平鎖,根據(jù)線程發(fā)出請求的順序獲取鎖。
非公平鎖的lock方法
????static?final?class?NonfairSync?extends?Sync?{
????????final?void?lock()?{
????????????//?2.?和公平鎖相比,這里會直接先進行一次CAS,成功就返回了
????????????if?(compareAndSetState(0,?1))
????????????????setExclusiveOwnerThread(Thread.currentThread());
????????????else
????????????????acquire(1);
????????}
????????//?AbstractQueuedSynchronizer.acquire(int?arg)
????????public?final?void?acquire(int?arg)?{
????????????if?(!tryAcquire(arg)?&&
????????????????acquireQueued(addWaiter(Node.EXCLUSIVE),?arg))
????????????????selfInterrupt();
????????}
????????protected?final?boolean?tryAcquire(int?acquires)?{
????????????return?nonfairTryAcquire(acquires);
????????}
????}
????/**
????*?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)?{
????????????//3.這里也是直接CAS,沒有判斷前面是否還有節(jié)點。
????????????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;
????}
非公平鎖的實現(xiàn)在剛進入lock方法時會直接使用一次CAS去嘗試獲取鎖,不成功才會到acquire方法中,如注釋2。而在nonfairTryAcquire方法中并沒有判斷是否有前驅(qū)節(jié)點在等待,直接CAS嘗試獲取鎖,如注釋3。由此實現(xiàn)了非公平鎖。
總結(jié)
非公平鎖和公平鎖的兩處不同:
非公平鎖在調(diào)用 lock 后,首先就會調(diào)用 CAS 進行一次搶鎖,如果這個時候恰巧鎖沒有被占用,那么直接就獲取到鎖返回了。
非公平鎖在 CAS 失敗后,和公平鎖一樣都會進入到 tryAcquire 方法,在 tryAcquire 方法中,如果發(fā)現(xiàn)鎖這個時候被釋放了(state == 0),非公平鎖會直接 CAS 搶鎖,但是公平鎖會判斷等待隊列是否有線程處于等待狀態(tài),如果有則不去搶鎖,乖乖排到后面。
公平鎖和非公平鎖就這兩點區(qū)別,如果這兩次 CAS 都不成功,那么后面非公平鎖和公平鎖是一樣的,都要進入到阻塞隊列等待喚醒。
相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。當然,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處于饑餓狀態(tài)。
原文鏈接:https://www.jianshu.com/p/2ada27eee90b
以上,便是今天的分享,希望大家喜歡,覺得內(nèi)容不錯的,歡迎「分享」「贊」或者點擊「在看」支持,謝謝各位。

