我們知道實(shí)現(xiàn)一把鎖要有如下幾個(gè)邏輯:
我們?cè)谥v解AQS的時(shí)候說過AQS基本負(fù)責(zé)了實(shí)現(xiàn)鎖的全部邏輯,唯獨(dú)線程搶鎖和線程釋放鎖的邏輯是交給子類來實(shí)現(xiàn)了,而ReentrantLock作為最常用的獨(dú)占鎖,其內(nèi)部就是包含了AQS的子類實(shí)現(xiàn)了線程搶鎖和釋放鎖的邏輯。
我們?cè)谑褂肦eentrantLock的時(shí)候一般只會(huì)使用如下方法:
ReentrantLock lock=new ReentrantLock(); lock.lock(); lock.unlock(); lock.tryLock(); Condition condition=lock.newCondition(); condition.await(); condition.signal(); condition.signalAll();
如果我們自己來實(shí)現(xiàn)一個(gè)鎖,那么如何設(shè)計(jì)呢?
根據(jù)AQS的邏輯,我們寫一個(gè)子類sync,這個(gè)類一定會(huì)調(diào)用父類的acquire方法進(jìn)行上鎖,同時(shí)重寫tryAcquire方法實(shí)現(xiàn)自己搶鎖邏輯,也一定會(huì)調(diào)用release方法進(jìn)行解鎖,同時(shí)重寫tryRelease方法實(shí)現(xiàn)釋放鎖邏輯。
那么ReentrantLock是怎么實(shí)現(xiàn)的呢?
ReentrantLock的實(shí)現(xiàn)的類架構(gòu)如下,ReentrantLock對(duì)外提供作為一把鎖應(yīng)該具備的api,比如lock加鎖,unlock解鎖等等,而它內(nèi)部真正的實(shí)現(xiàn)是通過靜態(tài)內(nèi)部類sync實(shí)現(xiàn),sync是AQS的子類,是真正的鎖,因?yàn)檫@把鎖需要支持公平和非公平的特性,所以sync又有兩個(gè)子類FairSync和NonfairSync分別實(shí)現(xiàn)公平鎖和非公平鎖。
因?yàn)槭欠窆秸f的是搶鎖的時(shí)候是否公平,那兩個(gè)子類就要在上鎖方法acquire的調(diào)用和搶鎖方法tryAcquire的重寫上做文章。
公平鎖做了什么文章?
static final class FairSync extends Sync { final void lock() { acquire(1); } 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; } }
公平鎖比較簡(jiǎn)單,直接調(diào)用了父級(jí)類AQS的acquire方法,因?yàn)锳QS的鎖默認(rèn)就是公平的排隊(duì)策略。
重寫tryAcquire方法的邏輯為:
公平就公平在老老實(shí)實(shí)排隊(duì)。
非公平鎖做了什么文章?
static final class NonfairSync extends Sync { final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } //nonfairTryAcquire代碼在父類sync里面 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; }
非公平鎖也很簡(jiǎn)單,沒有直接調(diào)用了父級(jí)類AQS的acquire方法,而是先通過cas搶鎖,它不管等待隊(duì)列中有沒有其他線程在排隊(duì),直接搶鎖,這就體現(xiàn)了不公平。
它重寫tryAcquire方法的邏輯為:
公平鎖和非公平分別重寫了tryAcquire方法,來滿足公平和非公平的特性。那么tryAcquire方法也是需要子類重寫的,因?yàn)樗褪欠窆綗o關(guān),因此tryAcquire方法被抽象到sync類中重寫。
sync類中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; }
釋放鎖的邏輯如下:
釋放鎖往往和搶鎖邏輯是對(duì)應(yīng)的,每個(gè)子類搶鎖邏輯不同的話,釋放鎖的邏輯也會(huì)對(duì)應(yīng)不同。
接下來我們通過ReentrantLock的使用看下它的源碼實(shí)現(xiàn):
class X { private final ReentrantLock lock = new ReentrantLock(); Condition condition1=lock.newCondition(); Condition condition2=lock.newCondition(); public void m() { lock.lock(); try { if(條件1){ condition1.await(); } if(條件2){ condition2.await(); } } catch (InterruptedException e) { } finally { condition1.signal(); condition2.signal(); lock.unlock(); } } }
ReentrantLock類public void lock() { sync.lock(); }
NonfairSync 類中 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
FairSync 類中 final void lock() { acquire(1); }
公平鎖和非公平鎖中都實(shí)現(xiàn)了lock方法,公平鎖直接調(diào)用AQS的acquire,而非公平鎖先搶鎖,搶不到鎖再調(diào)用AQS的acquire方法進(jìn)行上鎖
進(jìn)入acquire方法后的邏輯我們就都知道了。
public void unlock() { sync.release(1);}
unlock方法內(nèi)直接調(diào)用了AQS的Release方法進(jìn)行解鎖的邏輯,進(jìn)入release方法后邏輯我們都已經(jīng)知道了,這里不再往下跟。
這個(gè)方法中直接調(diào)用了sync中的nonfairTryAcquire方法,這個(gè)就是非公平鎖實(shí)現(xiàn)的TryAcquire方法,只是名字改了而已。
tryLock方法的意思是嘗試搶鎖,是給程序員調(diào)用的嘗試搶鎖方法,如果搶鎖成功返回true,搶鎖失敗返回false,當(dāng)拿到搶鎖失敗的信號(hào)后,程序員可以做一些自己的策略。
如果沒有tryLock方法,而是直接調(diào)用lock方法,就會(huì)走到acquire方法中,Acquire方法中也會(huì)調(diào)用TryAcquire方法,只不過在這里如果獲取不到鎖就會(huì)入隊(duì)阻塞了,就沒有程序員參與的可能了。
在AQS那篇我們說過Condition是AQS中的條件隊(duì)列,可以按條件將一批線程由不可喚醒變?yōu)榭蓡拘选?span style="display:none">Zy628資訊網(wǎng)——每日最新資訊28at.com
ReentrantLock類 public Condition newCondition() { return sync.newCondition(); }
sync靜態(tài)內(nèi)部類final ConditionObject newCondition() { return new ConditionObject();}
sync提供了創(chuàng)建Condition對(duì)象的方法,意味著ReentrantLock也擁有Condition的能力。
我們下面說的ReentrantLock其實(shí)就是說AQS,因?yàn)樗耐綄?shí)現(xiàn)主要在AQS里面。
優(yōu)化前的synchronized性能很差,主要表現(xiàn)在兩個(gè)方面:
ReentrantLock是在jdk實(shí)現(xiàn)的,它申請(qǐng)互斥量就是對(duì)鎖標(biāo)識(shí)state的爭(zhēng)奪,它是通過cas方式實(shí)現(xiàn)。在java端實(shí)現(xiàn)。
對(duì)于爭(zhēng)奪不到資源的線程依然要阻塞掛起,但凡阻塞掛起都要依賴于操作系統(tǒng)底層,這一步的用戶態(tài)到內(nèi)核態(tài)的切換是避免不了的。
因此在單線程進(jìn)入代碼塊的時(shí)候,效率是很高的,因此我們說ReentrantLock性能高于原始的synchronized:
兩個(gè)都是常用的典型的獨(dú)占鎖。
synchronized不需要手動(dòng)釋放鎖,ReentrantLock需要手動(dòng)釋放鎖,需要考慮異常對(duì)釋放鎖的影響避免異常導(dǎo)致線程一直持有鎖。
以下是兩個(gè)鎖的使用方式:
class X { private final ReentrantLock lock = new ReentrantLock(); Condition condition1=lock.newCondition(); Condition condition2=lock.newCondition(); public void m() { lock.lock(); try { if(1==2){ condition1.await(); } if(1==3){ condition2.await(); } } catch (InterruptedException e) { } finally { condition1.signal(); condition2.signal(); lock.unlock(); } } }
class X { private final testtest sync=new testtest();; public void m() throws InterruptedException { synchronized(sync){ if(1==2){ sync.wait(); } sync.notify(); sync.notifyAll(); } } }
對(duì)比代碼及特性說明:
本文鏈接:http://www.tebozhan.com/showinfo-26-15183-0.html被問到ReentrantLock你真的能答好嗎?
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com