ReentrantLock 提供了公平鎖和非公平鎖,只須要在構造方法中使用一個 boolean
參數便可。默認非公平鎖。java
今天從源碼層面看看區別和具體實現。ui
ReentrantLock
內部有一個抽象類 Sync
,繼承了 AQS。spa
而公平鎖的實現就是 FairSync
,非公平鎖的實現就是 NodFairSync
。線程
兩把鎖的區別在於lock
方法的實現。設計
final void lock() {
acquire(1);
}
複製代碼
調用的是 AQS 的acquire
方法,熟悉 AQS 的同窗都知道,AQS 會回調子類的 tryAcquire
方法,看看公平鎖的tryAcquire
實現。code
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;
}
複製代碼
說下邏輯:cdn
注意上面的第二步:判斷 AQS 隊列中是否有等待的線程。blog
這就是公平的體現。繼承
再看看非公平鎖的區別。隊列
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
複製代碼
lock 方法就不一樣了,很衝動的一個方法,直接使用 CAS 獲取鎖,若是成功了,就設置鎖的持有線程爲本身。很快速。因此默認使用了非公平鎖。
若是失敗了,就調用 AQS 的 acquire
方法。固然,咱們看的仍是tryAcquire
方法,在上面的代碼中,tryAcquire
方法調用了父類Sync
的 nonfairTryAcquire
,爲何在父類中呢?
在ReentrantLock
的 tryLock
方法中,也調用了該方法。由於這個方法是快速返回的。該方法不會讓等待時間久的線程獲取鎖。符合 tryLock
的設計。
方法實現以下:
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;
}
複製代碼
該方法相比較公平鎖的 tryAcquire
方法,少了一步判斷 AQS 隊列中是否有等待的線程的操做。
他要作的就是直接搶鎖,不讓給隊列裏那些等待時間長的。
搶不到再進入隊列。等待他的前置節點喚醒他。這個過程是公平的。
ReentrantLock
中的公平鎖和非公平鎖的區別就在於:調用 lock
方法獲取鎖的時候 要不要判斷 AQS 隊列中是否有等待的線程
,公平鎖爲了讓每個線程都均衡的使用鎖,就須要判斷,若是有,讓給他,非公平鎖很霸道,不讓不讓就不讓。
但若是失敗了,進入隊列了,進會按照 AQS 的邏輯來,總體順序就是公平的。
還有個注意的地方就是:ReentrantLock
的 tryLock(無超時機制)
方法使用的非公平策略。符合他的設計。
而 tryLock(long timeout, TimeUnit unit)
方法則會根據 Sync 的具體實現來調用。不會衝動的調用 nonfairTryAcquire
方法。