簡單介紹
ReentrantLock 是一個可重入的獨占鎖
-
可重入
同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然可以獲取該鎖的代碼
該特性帶來的兩個問題:- 如何識別獲取鎖的線程是否為當前占據(jù)鎖的線程
- 線程重復(fù) n 次獲取了鎖,需要釋放 n 次鎖,否則會導致別的線程無法獲得鎖
-
獨占
一次只能被一個線程所持有
類型
private final Sync sync;
ReentrantLock 的內(nèi)部類 Sync 繼承了 AQS(AbstractQueuedSynchronizer),并且有公平鎖 FairSync 和 非公平鎖 NonfaireSync 兩個字類,ReentrantLock 的獲取與釋放鎖操作都是委托給該同步組件來實現(xiàn)的
(注:該篇文章里所有 AQS 相關(guān)的內(nèi)容會再寫一篇相關(guān)的文章,這里不詳細介紹)
- 公平鎖
是指當鎖可用時,在鎖上等待時間最長的線程將獲取鎖的使用權(quán)(先來先得)
使用有參構(gòu)造方法,傳入true創(chuàng)建公平鎖public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } - 非公平鎖
隨機分配使用權(quán),使用ReentrantLock無參的構(gòu)造函數(shù),默認創(chuàng)建的是非公平鎖public ReentrantLock() { sync = new NonfairSync(); }
常用方法介紹
-
lock()方法獲取鎖,如果獲取不到鎖,則當前線程在獲取到鎖之前都不可調(diào)度(不響應(yīng)中斷)public void lock() { sync.lock(); } -
lockInterruptibly()方法獲取鎖,則當前線程在獲取到鎖之前都不可調(diào)度,除非有其他線程中斷了當前線程(響應(yīng)中斷)public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } -
tryLock()方法獲取鎖,如果調(diào)用的時候能夠獲取鎖,那么就獲取鎖并且返回true,如果當前的鎖無法獲取到,那么這個方法會立刻返回falsepublic boolean tryLock() { return sync.nonfairTryAcquire(1); } -
tryLock(long timeout, TimeUnit unit)方法獲取鎖,在指定時間內(nèi)嘗試獲取鎖。如果可以獲取鎖,則獲取鎖并返回true,如果無法獲取鎖,則當前線程變?yōu)椴豢烧{(diào)度,除非當前線程被中斷或者到了指定的等待時間public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } -
unlock()釋放鎖,釋放當前線程占用的鎖。注意:獲取了幾次鎖,就要釋放幾次鎖public void unlock() { sync.release(1); } -
newCondition()方法,返回一個與當前的鎖關(guān)聯(lián)的條件變量。在使用這個條件變量之前,當前線程必須占用鎖。調(diào)用Condition的await方法,會在等待之前原子地釋放鎖,并在等待被喚醒后原子的獲取鎖public Condition newCondition() { return sync.newCondition(); } -
isHeldByCurrentThread()方法,查詢當前線程是否保持鎖定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
當我們調(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操作,去嘗試搶占該鎖。如果成功,就把當前線程設(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è)置當前線程為獨占線程 return true; } } else if (current == getExclusiveOwnerThread()) {// 如果鎖的狀態(tài)不為 0,判斷該線程是否是獨占線程(可重入) int nextc = c + acquires; // 如果當前線程是獨占線程,則增加狀態(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è)置當前線程為獨占線程,因為隊列中的線程和新線程都可以CAS獲取鎖不需要排隊,所以是非公平鎖 -
status不為0時:代表鎖已經(jīng)被占有,如果當前線程是占有鎖的線程(
current == getExclusiveOwnerThread()為true),更新state,意味著當前線程又一次的獲取了鎖,這就是可重入。
-
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è)置當前線程為獨占線程 return true; } } else if (current == getExclusiveOwnerThread()) { // 如果鎖的狀態(tài)不為 0,判斷該線程是否是獨占線程(可重入) int nextc = c + acquires; // 如果當前線程是獨占線程,則增加狀態(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時 新線程需要判斷有沒有線程在排隊獲取鎖,只有當沒有的時候才會去嘗試搶占鎖,如果有線程在排隊,新線程也會被加入到排隊的隊列中去 -
-
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()) // 如果當前線程不是獨占線程則拋異常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 當狀態(tài)值為 0 ,鎖釋放 free = true; setExclusiveOwnerThread(null); // 將獨占線程設(shè)置為 null } setState(c); // 狀態(tài)變量賦值 return free; }tryRelease成功,下一個節(jié)點的線程被喚醒,被喚醒的線程就會進入acquireQueued流程中,去獲取鎖