
經過前面的三節,相信你對ReentrantLock底層的AQS原理已經很清楚了,接下來給大家介紹幾個ReentrantLock中的幾個概念:
- 公平,非公平鎖的概念
- ReentrantLock是如何實作非公平和公平的?
- 可重入鎖又是什么東西?
公平鎖 Vs 非公平鎖
公平鎖 Vs 非公平鎖
當你掌握了ReentrantLock加鎖,加鎖失敗入隊,釋放鎖的原理后,其實在ReenrantLock中還需要搞明白幾個概念,比如獨占鎖、共享鎖、可重入鎖,公平鎖和非公平鎖這些都是什么意思,
這一小節,我們先來聊聊公平和非公平鎖,
什么是公平鎖?什么又是非公平鎖呢?這里給大家舉個例子:
相信你肯定有過排隊的經歷,比如你給女朋友排隊買過奶茶,但是你排隊排的好好的,突然當有個老板親戚或者關系戶過來插了一個隊,你是什么感覺?是不是感覺不太公平,但是有的關系戶也很有修養,不會插隊,會老老實實去排隊,這就很公平了, 因為先來后到么,
這其實就是公平和非公平的鎖的意思,你可以想想,還是上面的例子,執行緒2在排隊了,此時執行緒1釋放了鎖,可是突然來了一個執行緒3,也來加鎖,是不是可能在執行緒2出隊的程序中,執行緒3搶到鎖,這就是非公平的,執行緒3插隊了,沒有老老實實排隊,
但是如果執行緒3,老老實實的排隊,進入AQS的佇列中,這樣就是公平鎖,如下圖所示:

ReentrantLock是如何實作非公平和公平的?
ReentrantLock是如何實作非公平和公平的?
具體代碼是怎么做到呢?核心是通過兩個Sync的子類,FairSync和NonfairSync,從名字上看,你就應該知道,這兩個類是公平和非公平AQS的Sync組件意思,
大家可以它們兩個類的找找不同看看:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
? if (compareAndSetState(0, 1))
? setExclusiveOwnerThread(Thread.currentThread());
? else
? acquire(1);
}
protected final boolean tryAcquire(int acquires) {
? return nonfairTryAcquire(acquires);
}
}
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;
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
? acquire(1);
}
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;
}
}
首先是lock方法,區別就是在一個if判斷,非公平的鎖NonfairSync會多了一個判斷,先嘗試來加個鎖,
這個區別是什么意思呢?你可以理解為如果執行緒1釋放了,別人過來加鎖,直接先嘗試插個隊的意思,有可能AQS佇列中的執行緒2還沒被喚醒了,被別人搶走了鎖,讓別的執行緒加鎖成功了,
如何把鎖給釋放掉,另外一個是如果鎖徹底釋放了以后,如何讓佇列中的隊頭的那個執行緒來喚醒嘗試獲取鎖,

而另一個方法,嘗試加鎖,唯一的區別是一個if條件
hasQueuedPredecessors()
這方法從名字就能看出來,判斷下佇列中有沒有有元素,代碼如下:
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
也就是說,在公平鎖的嘗試加鎖的代碼中,有一個限制如果有人排隊,其他執行緒就不能插隊加鎖,所以就算執行緒1釋放鎖,執行緒3過來加鎖,由于lock方法沒有了非公平鎖的if(上來嘗試CAS修改state,加鎖的代碼),執行緒3就只能入隊,如果執行緒3執行到嘗試獲取鎖的代碼時,公平鎖比非公平鎖的代碼多了一個判斷,判斷佇列中是否有等待執行緒,有的話也只能乖乖排隊,如下圖所示:

可重入鎖 Vs 不可重入鎖
可重入鎖 Vs 不可重入鎖
之前提到,ReentrantLock涉及了一些鎖的概念,講過了公平和非公平鎖的概念后,今天我們最后聊一下可重入鎖,
其實這個比較好理解,ReentrantLock通過AQS的state變數巧妙的實作了可重入加鎖,如果是同一個執行緒呼叫了lock方法,加鎖,state會在現有值上加+1,每再次加一次鎖,就是一次可重入,所以就加鎖可重入鎖,也就是說:
同一個執行緒可以使用同一個ReentrantLock進行反復加鎖,
另外,釋放鎖的話,肯定需要釋放所多次,同一個執行緒加鎖了幾次,就需要釋放幾次,需要將state值恢復為0才算真正的釋放鎖,別的執行緒才能獲取到,
由于比較簡單,就不帶大家看原始碼實作了,你可以自己在原始碼中找找,核心還是掌握AQS加鎖釋放鎖的原理最重要,

獨占鎖 VS 共享鎖
獨占鎖 VS 共享鎖
至于獨占和共享鎖的概念之后講解讀寫鎖的時候會提到,這里先簡單的講一下,
所謂獨占鎖,就是只要有一個執行緒加鎖,其他人都得靠邊站,這把鎖屬于某個執行緒獨占,這就是獨占鎖,
默認reentrantLock.lock創建的鎖是什么的呢?非公平的可重入獨占鎖!
共享鎖是什么意思呢?意思就是可以和別的執行緒同時持有一把鎖,比如之后要將的讀寫鎖,執行緒1加了讀鎖,執行緒2還是可以加讀鎖的,它們共享一把鎖,這樣的鎖就是一把共享鎖,
當然讀寫鎖之間是有一些互斥關系的,所以下一節我們就來探索下,如何使用讀寫鎖、讀寫鎖的原理具體是什么、以及讀寫鎖的互斥關系,
小結&思考
小結&思考
其實這一節并沒有什么特別負責復雜的知識,主要帶大家看了 ReentrantLock的重入實作
核心思想就是通過代碼執行順序,CAS操作順序和一個if判斷佇列是否有等待執行緒實作的,
其次就是介紹了幾個概念,可重入鎖、公平、非公平鎖、獨占和共享鎖分別是什么意思,
ReentrantLock的原理其實到這里我們大體就分析完成了,你起碼掌握了 ReentrantLock的基于抽象類封裝3個組件(變數)的操作設計,
之前我們提到的synchronized的底層ObjectMonitor,其實是不是也是用過這個思想設計的,只不過一個是C++封裝的ObjectMonitor物件,一個是Java封裝的ReentrantLock物件,
大家當學完一個技術的原理和或者原始碼,或者做完一個專案后,一定要學會進行思考,思考之后才能更好的應用這個技術、更好的解決問題,
另外有興趣的同學可以去深究下,它使用的CAS底層JVM C++語言如何的實作,為什么用LockSupport.park掛起執行緒,LockSupport的park方法實作等等,,
下一節我們開始研究下ReentrantReadWriteLock的實作原理,我們下一節見!
本文由博客群發一文多發等運營工具平臺 OpenWrite 發布
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/401381.html
標籤:Java
上一篇:參與Bean的生命周期
