簡單介紹
ReentrantLock
是一個可重入的獨占鎖
-
可重入
同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然可以獲取該鎖的代碼
該特性帶來的兩個問題:- 如何識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程
- 線程重復(fù) n 次獲取了鎖纬凤,需要釋放 n 次鎖饺窿,否則會導(dǎo)致別的線程無法獲得鎖
-
獨占
一次只能被一個線程所持有
類型
private final Sync sync;
ReentrantLock
的內(nèi)部類 Sync
繼承了 AQS
(AbstractQueuedSynchronizer
),并且有公平鎖 FairSync
和 非公平鎖 NonfaireSync
兩個字類移斩,ReentrantLock
的獲取與釋放鎖操作都是委托給該同步組件來實現(xiàn)的
(注:該篇文章里所有 AQS
相關(guān)的內(nèi)容會再寫一篇相關(guān)的文章肚医,這里不詳細(xì)介紹)
- 公平鎖
是指當(dāng)鎖可用時,在鎖上等待時間最長的線程將獲取鎖的使用權(quán)(先來先得)
使用有參構(gòu)造方法向瓷,傳入true
創(chuàng)建公平鎖public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
- 非公平鎖
隨機分配使用權(quán)肠套,使用ReentrantLock
無參的構(gòu)造函數(shù),默認(rèn)創(chuàng)建的是非公平鎖public ReentrantLock() { sync = new NonfairSync(); }
常用方法介紹
-
lock()
方法獲取鎖猖任,如果獲取不到鎖你稚,則當(dāng)前線程在獲取到鎖之前都不可調(diào)度(不響應(yīng)中斷)public void lock() { sync.lock(); }
-
lockInterruptibly()
方法獲取鎖,則當(dāng)前線程在獲取到鎖之前都不可調(diào)度朱躺,除非有其他線程中斷了當(dāng)前線程(響應(yīng)中斷)public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
-
tryLock()
方法獲取鎖刁赖,如果調(diào)用的時候能夠獲取鎖,那么就獲取鎖并且返回true
长搀,如果當(dāng)前的鎖無法獲取到宇弛,那么這個方法會立刻返回false
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
-
tryLock(long timeout, TimeUnit unit)
方法獲取鎖,在指定時間內(nèi)嘗試獲取鎖源请。如果可以獲取鎖枪芒,則獲取鎖并返回true
彻况,如果無法獲取鎖,則當(dāng)前線程變?yōu)椴豢烧{(diào)度舅踪,除非當(dāng)前線程被中斷或者到了指定的等待時間public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
-
unlock()
釋放鎖纽甘,釋放當(dāng)前線程占用的鎖。注意:獲取了幾次鎖抽碌,就要釋放幾次鎖public void unlock() { sync.release(1); }
-
newCondition()
方法悍赢,返回一個與當(dāng)前的鎖關(guān)聯(lián)的條件變量。在使用這個條件變量之前货徙,當(dāng)前線程必須占用鎖左权。調(diào)用Condition
的await
方法,會在等待之前原子地釋放鎖破婆,并在等待被喚醒后原子的獲取鎖public Condition newCondition() { return sync.newCondition(); }
-
isHeldByCurrentThread()
方法涮总,查詢當(dāng)前線程是否保持鎖定public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); }
-
isLocked()
方法胸囱,查詢該鎖是否已經(jīng)被鎖定public boolean isLocked() { return sync.isLocked(); }
-
isFair()
方法祷舀,判斷鎖是公平鎖還是非公平鎖public final boolean isFair() { return sync instanceof FairSync; }
Sync 內(nèi)部類
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
Sync
是一個抽象類型,它繼承 AbstractQueuedSynchronizer
烹笔,這個 AbstractQueuedSynchronizer
是一個模板類裳扯,它實現(xiàn)了許多和鎖相關(guān)的功能,并提供了鉤子方法供用戶實現(xiàn)谤职,比如 tryAcquire
饰豺,tryRelease
等
static final class NonfairSync extends Sync {
final void lock() {
...
}
}
static final class FairSync extends Sync {
final void lock() {
...
}
}
NonfairSync
和 FairSync
兩個類繼承自 Sync
,實現(xiàn)了 lock
方法
-
lock
當(dāng)我們調(diào)用ReentrantLock
的lock
方法的時候允蜈,實際上是調(diào)用了NonfairSync
或者FairSync
的lock
方法-
NonfairSync
非公平鎖的lock
實現(xiàn)final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
CAS
操作冤吨,去嘗試搶占該鎖。如果成功饶套,就把當(dāng)前線程設(shè)置在這個鎖上漩蟆,表示搶占成功。如果失敗妓蛮,則調(diào)用acquire
模板方法怠李,等待搶占
acquire
方法里調(diào)用了tryAcquire(int arg)
方法,NonfairSync
的tryAcquire
實際上又調(diào)用的Sync
的nonfairTryAcquire
方法蛤克,如下final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 判斷鎖的狀態(tài)是不是 0捺癞,如果是,則嘗試去原子搶占這個鎖 if (compareAndSetState(0, acquires)) { // 如果搶占到了构挤,把狀態(tài)設(shè)置為1 setExclusiveOwnerThread(current); // 并且設(shè)置當(dāng)前線程為獨占線程 return true; } } else if (current == getExclusiveOwnerThread()) {// 如果鎖的狀態(tài)不為 0髓介,判斷該線程是否是獨占線程(可重入) int nextc = c + acquires; // 如果當(dāng)前線程是獨占線程,則增加狀態(tài)變量的值 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); // 給狀態(tài)變量賦值 return true; } return false; }
tryAcquire
一旦返回false
筋现,就會則進入acquireQueued
流程版保,此段代碼中鎖的獲取可以分為兩種情況:-
state為0時:代表鎖已經(jīng)釋放呜笑,可以去獲取,所以使用
CAS
去獲取鎖彻犁,如果獲取成功叫胁,則代表競爭鎖成功,調(diào)用setExclusiveOwnerThread
設(shè)置當(dāng)前線程為獨占線程汞幢,因為隊列中的線程和新線程都可以CAS
獲取鎖不需要排隊驼鹅,所以是非公平鎖 -
status不為0時:代表鎖已經(jīng)被占有,如果當(dāng)前線程是占有鎖的線程(
current == getExclusiveOwnerThread()
為true
),更新state森篷,意味著當(dāng)前線程又一次的獲取了鎖输钩,這就是可重入。
-
FairSync
公平鎖的lock
實現(xiàn)final void lock() { acquire(1); }
acquire
方法里同樣調(diào)用了tryAcquire(int arg)
方法仲智,FairSync
的tryAcquire
方法實現(xiàn)如下:protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 判斷鎖的狀態(tài)是不是 0买乃,如果是 0,再判斷是否有線程在排隊獲取鎖 if (!hasQueuedPredecessors() && // 如果沒有線程在排隊獲取鎖則嘗試原子搶占鎖 compareAndSetState(0, acquires)) { // 如果搶占到了钓辆,把狀態(tài)設(shè)置為1 setExclusiveOwnerThread(current); // 并且設(shè)置當(dāng)前線程為獨占線程 return true; } } else if (current == getExclusiveOwnerThread()) { // 如果鎖的狀態(tài)不為 0剪验,判斷該線程是否是獨占線程(可重入) int nextc = c + acquires; // 如果當(dāng)前線程是獨占線程,則增加狀態(tài)變量的值 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); // 給狀態(tài)變量賦值 return true; } return false; }
tryAcquire
一旦返回false
前联,就會則進入acquireQueued
流程功戚,公平鎖獲取鎖的過程與非公平鎖不一樣的地方在 state為0時 新線程需要判斷有沒有線程在排隊獲取鎖,只有當(dāng)沒有的時候才會去嘗試搶占鎖似嗤,如果有線程在排隊啸臀,新線程也會被加入到排隊的隊列中去 -
-
unlock
unlock
方法,其實是直接調(diào)用AbstractQueuedSynchronizer
的release
操作烁落,release
方法先調(diào)用了tryRelease
方法乘粒,Sync
的tryRelease
方法實現(xiàn)如下:
一旦protected final boolean tryRelease(int releases) { int c = getState() - releases; //狀態(tài)變量值減少,這里是考慮到可重入鎖可能自身會多次占用鎖 if (Thread.currentThread() != getExclusiveOwnerThread()) // 如果當(dāng)前線程不是獨占線程則拋異常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 當(dāng)狀態(tài)值為 0 伤塌,鎖釋放 free = true; setExclusiveOwnerThread(null); // 將獨占線程設(shè)置為 null } setState(c); // 狀態(tài)變量賦值 return free; }
tryRelease
成功灯萍,下一個節(jié)點的線程被喚醒,被喚醒的線程就會進入acquireQueued
流程中寸谜,去獲取鎖