重入鎖ReentrantLock,顧名思義厌衔,就是支持重進入的鎖,它表示該鎖能夠支持一個線程對資源的重復(fù)加鎖夺艰。除此之外芋哭,該鎖的還支持獲取鎖時的公平和非公平性選擇。
ReentrantLock雖然沒能像synchronized關(guān)鍵字一樣支持隱式的重進入郁副,但是在調(diào)用lock()方法時减牺,已經(jīng)獲取到鎖的線程,能夠再次調(diào)用lock()方法獲取鎖而不被阻塞存谎。
ReentrantLock提供了一個構(gòu)造函數(shù)拔疚,能夠控制鎖是否是公平的(如果在絕對時間上,先對鎖進行獲取的請求一定先被滿足既荚,那么這個鎖是公平的稚失,反之,是不公平的)固以。事實上墩虹,公平的鎖機制往往沒有非公平的效率高,但是憨琳,并不是任何場景都是以TPS作為唯一的指標诫钓,公平鎖能夠減少“饑餓”發(fā)生的概率,等待越久的請求越是能夠得到優(yōu)先滿足篙螟。
1 實現(xiàn)重入
- 線程再次獲取鎖
鎖需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程菌湃,如果是,則再次成功獲取遍略。 - 鎖的最終釋放
線程重復(fù)n次獲取了鎖惧所,隨后在第n次釋放該鎖后,其他線程能夠獲取到該鎖绪杏。鎖的最終釋放要求鎖對于獲取進行計數(shù)自增下愈,計數(shù)表示當(dāng)前鎖被重復(fù)獲取的次數(shù),而鎖被釋放時蕾久,計數(shù)自減势似,當(dāng)計數(shù)等于0時表示鎖已經(jīng)成功釋放。
ReentrantLock的nonfairTryAcquire方法:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果同步狀態(tài)為0僧著,即沒有線程獲取鎖
if (c == 0) {
// 嘗試獲取鎖
if (compareAndSetState(0, acquires)) {
// 獲取成功履因,設(shè)當(dāng)前擁有鎖的線程為自己
setExclusiveOwnerThread(current);
return true;
}
// 如果鎖已被其他線程獲取,則檢查當(dāng)前線程是否和獲取鎖的線程相同
} else if (current == getExclusiveOwnerThread()) {
// 如果相同盹愚,則增加同步狀態(tài)栅迄,表示重入
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
ReentrantLock的tryRelease方法:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 當(dāng)同步狀態(tài)為0時,將占有線程設(shè)置為null皆怕,并返回true毅舆,表示釋放成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
2 公平與非公平獲取鎖的區(qū)別
上一小節(jié)中介紹的nonfairTryAcquire(int acquires)方法西篓,對于非公平鎖,只要CAS設(shè)置同步狀態(tài)成功朗兵,則表示當(dāng)前線程獲取了鎖污淋,而公平鎖則不同,代碼如下所示:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 唯一不同的位置為判斷條件多了hasQueuedPredecessors()方法
// 即加入了同步隊列中當(dāng)前節(jié)點是否有前驅(qū)節(jié)點的判斷余掖,如果該
// 方法返回true,則表示有線程比當(dāng)前線程更早地請求獲取鎖
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;
}
3 性能對比
公平性鎖保證了鎖的獲取按照FIFO原則礁鲁,而代價是進行大量的線程切換盐欺。
非公平性鎖雖然可能造成線程“饑餓”,但極少的線程切換仅醇,保證了其更大的吞吐量冗美。