目錄
- 1. 公平鎖和非公平鎖
- 1.1 基本概念
- 1.2 ReentrantLock 的公平鎖和非公平鎖
- 2. 加鎖流程
- 2.1 ReentrantLock 和 AQS 的關系
- 2.2 公平鎖-加鎖流程
- 2.3 非公平鎖-加鎖流程
- 2.4 加鎖流程和性能的關系
- 3. 面試問題模擬
- 參考文獻
1. 公平鎖和非公平鎖
1.1 基本概念
- 公平鎖:執行緒按照到來的先后順序,排隊等待使用資源,
- 非公平鎖:執行緒不一定按照先后順序使用資源,而是可能出現“插隊”的情況,
拿游樂場等待娛樂專案舉例,普通游客只能按照先后順序排隊等待使用游樂設施,這就是公平鎖
,但是普通入口加上優速通,顯然VIP游客可以快人一步,這就有點非公平鎖
的意思了,
1.2 ReentrantLock 的公平鎖和非公平鎖
在《【后端面經-Java】Synchronize和ReentrantLock區別》這篇博客中,我們對比過synchronized
和ReentrantLock
的區別,其中synchronized
是一種非公平鎖
,而ReentrantLock
默認是非公平鎖
,但是也可設定為公平鎖
,
具體設定方式如下:
//生成一個公平鎖
static Lock lock = new ReentrantLock(true);
//生成一個非公平鎖
static Lock lock = new ReentrantLock(false);
static Lock lock = new ReentrantLock();//默認引數就是false,這種寫法也可
通過更改建構式中的引數,我們可以修改ReentrantLock
的鎖型別,true
表示公平鎖,false
表示非公平鎖,建構式具體代碼如下所示:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//FairSync表示公平鎖,NonfairSync表示非公平鎖
}
2. 加鎖流程
2.1 ReentrantLock 和 AQS 的關系
在【后端面經-Java】AQS詳解這篇博客中,我們詳細講解了AQS
的原理,其中提到了
AQS定義了一套多執行緒訪問共享資源的同步器框架,許多同步類實作都依賴于它,如常用的ReentrantLock,
可就是說,ReentrantLock
也是通過AQS
來實作的,而自定義同步鎖需要實作AQS
框架中的tryAcquire()
和tryRelease()
方法或者tryAcquireShared()
和tryReleaseShared()
方法,
因此,ReentrantLock
的加鎖流程我們可用查看tryAcquire()
方法了解,
2.2 公平鎖-加鎖流程
公平鎖的tryAcquire()
方法原始碼如下所示:
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;
}
代碼流程如下所示:
- 查看是否有其他執行緒在等待資源,
- 如果沒有其他執行緒在等待,查看資源是否可用,如果資源可用,直接獲取資源,
- 如果有其他執行緒在等待或者資源不可用(正在被使用),執行緒乖乖排到隊尾,并切換為等待喚醒的休眠態,
2.3 非公平鎖-加鎖流程
非公平鎖的tryAcquire()
方法原始碼如下所示:
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;
}
和公平鎖
相比,非公平鎖
的加鎖流程只是少了對其他執行緒是否等待的判斷,因此,非公平鎖
的加鎖流程如下所示:
- 查看資源是否可用,如果資源可用,直接獲取資源,
- 如果資源不可用,不需要管是否有執行緒在排隊,還是排在等待佇列隊尾,
2.4 加鎖流程和性能的關系
公平鎖能保證執行緒獲取資源的公平性,但是性能較低;
而非公平鎖雖然無法保障公平性,但是性能更高,因此在大多數情況下,我們都會使用非公平鎖,
- 關于“公平鎖性能低,非公平鎖性能高”的解釋
理解這個結論,我們需要知道公平鎖和非公平鎖申請資源的流程,- 對于公平鎖,當一個執行緒創建之后,它會看是否有其他執行緒在等待資源,也就是看看
排隊隊伍里面有沒有人
,如果有其他執行緒在等待,它就乖乖排到隊尾,并切換為等待喚醒的休眠態,而如果沒有其他執行緒在等待,它就直接獲取資源, - 對于非公平鎖,當一個執行緒創建之后,它會直接試著去獲取資源,不管隊伍里有沒有人,如果這個時候正好資源被釋放,那么非公平鎖因為是搶著使用資源的,提出資源申請比首個在佇列中等待的執行緒要早,因此資源會直接給它,如果獲取資源失敗,它才會乖乖去隊尾排隊等待,
- 對于公平鎖,當一個執行緒創建之后,它會看是否有其他執行緒在等待資源,也就是看看
對于執行緒狀態的切換,從休眠態到就緒態,這部分是需要時間進行背景關系切換的,因此,公平鎖每次都直接進入休眠態等待被喚醒,這本身就是很耗費時間的事情,因此我們才說公平鎖性能低,非公平鎖性能高,
(非公平鎖雖然不公平,但是性能高,真的是很諷刺的一種情況吶,)
3. 面試問題模擬
Q:公平鎖是什么?加鎖流程是什么?
A:公平鎖是指在資源獲取程序中,執行緒按照到來順序排隊使用資源的一種鎖機制,而非公平鎖則可能出現不按順序的隨機獲取情況,
公平鎖的加鎖流程體現在tryAcquire()
原始碼部分,當一個執行緒節點創建之后,它會判斷當前是否有其他執行緒在等待以及資源是否可用,如果兩個條件都滿足,它則獲取資源,如果不滿足,它則乖乖排到隊尾,等待被喚醒,
參考文獻
- 面試突擊46:公平鎖和非公平鎖有什么區別?
- 講一講公平鎖和非公平鎖,為什么要“非公平”?
- ReentrantLock 加鎖程序原始碼詳解
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556285.html
標籤:Java
上一篇:JSP
下一篇:返回列表