一、類圖結構

ReentrantLock是一個可重入鎖,只有一個執行緒可以獲取到該鎖,其他執行緒想要獲取該鎖的時候會被放到AQS佇列中, 從類圖中可以看到實作了Lock介面,內含一個Sync型別變數,該型別是繼承自AQS抽象類,同時又有兩個繼承了類,分別為公平鎖和非公平鎖,
Sync sync;
public ReentrantLock() {
sync = new NonfairLock();
}
public ReentrantLock(boolean fair) {
sync = fair? new FairLock():new NonfairLock();
}
這里的AQS中state變數代表可重入的次數,0為該鎖為空閑階段,1為該鎖被某執行緒占用,當該執行緒再次重入的時候,該值就會遞增;釋放的時候,該值就會遞減,直到遞減為0,才表示該鎖已完全釋放,其他執行緒才有拿到的機會,
二、獲取鎖
從類中可以看到獲取鎖的方法是lock(),下面是實作
public void lock() {
sync.lock();
}
直接把鎖交給了Sync的實作類,這就取決于使用構造方法創建的是公平鎖還是非公平鎖, 下面看一下非公平鎖lock的實作鎖
public void lock() {
// CAS設定狀態值
if(compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread())
}else {
acquire(1);
}
}
使用CAS演算法,修改state的值為1,同時呼叫setExclusiveOwnerThread方法來設定當前執行緒為占用的執行緒,如果被占用了,那就呼叫acquire方法,傳遞引數為1,下面看一下這個方法的原始碼,
public final void acquire(int arg) {
// 呼叫ReentrantLock重寫的tryAcquire方法
if(!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE) ,arg)) {
selfInterrupt();
}
}
之前說過AQS并沒有提供可用的tryAcquire方法,tryAcquire方法需要子類自定定制化,所以上述的代碼會呼叫ReentrantLock重寫的tryAquire方法,我們先看一下非公平鎖的代碼
protected final boolean tryAcuqire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if(c == 0) {
// 當前AQS狀態值為0,也就是沒有執行緒獲取到了該鎖,那么我們讓當前執行緒占用該鎖
if(compareAndSetState(0,acquires)) {
setExlusiveOwnerThread(current);
return true;
}else if(current == getExlusiveThread()){
// 該鎖已經被當前執行緒占用,那么也就是重入的情況
int nextc = c + acquires;
if(nextc <0) { // 這種情況就是重入的次數太多了,超過了int正值的范疇
throw new Error("Maxium lock count exceeded");
}
setState(nextc);
return true;
}else {
return false;
}
}
}
}
代碼(4)會查看當前鎖的狀態值是否為0,為0則說明當前該鎖空閑,那么就嘗試CAS獲取該鎖,將AQS的狀態值從0設定為1,并設定當前鎖的持有者為當前執行緒,然后回傳true,如果當前狀態值不為0則說明該鎖已經被某個執行緒所擁有,,所以代碼(5)查看當前執行緒是否為該擁有者,如果當前執行緒是該鎖的持有者,則狀態值為1,然后回傳true,這里需要注意的是,nextc<0說明可重入次數溢位了,如果當前該執行緒不是鎖的持有者則回傳false,然后其會放入到AQS阻塞佇列, 上面是非公平鎖的實作代碼,回過頭來看看非公平鎖體現在哪里,首先非公平是說嘗試獲取鎖的執行緒并不一定比后嘗試獲取鎖的執行緒優先獲取鎖, 這里假設執行緒A呼叫了lock()方法執行到nonfairTryAcquire的代碼(4),發現當前狀態值不為0,所以執行代碼(5),發現當前執行緒不是執行緒持有者,則執行代碼(6)回傳false,然后當前執行緒放入AQS阻塞佇列, 這時候執行緒B也呼叫了lock方法執行到nonfairTryAcquire的代碼(4),發現當前狀態值為0了(假設占有該鎖的其他下稱釋放了該鎖),所以通過CAS設定獲取了該鎖,明明是執行緒A先請求獲取該鎖,這就是非公平鎖的體現,這里執行緒B在獲取鎖之前并沒有查看當前AQS佇列里面是否有比自己更早請求該鎖的執行緒,而是使用了搶奪策略,那么下面看看公平鎖是怎么實作公平的,公平鎖的話只需要看FairSync重寫的tryAcquire方法,
protected final boolean tryAcuqire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// (7)當前AQS狀態值為0
if(c == 0) {
// (8)公平性策略
if(!hasQueuedPredecessors() && compareAndSetState(0,acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// (9)當前執行緒是該鎖持有者
else if(current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if(nextc<0) {
throw new Error("Maximum lock count exceeded");
}
setState(nextc);
return true;
}
return false;
}
三、原始碼:
所在包:com.ruigege.ConcurrentListSouceCodeAnalysis5 https://github.com/ruigege66/ConcurrentJavaCSDN:https://blog.csdn.net/weixin_44630050 博客園:https://www.cnblogs.com/ruigege0000/ 歡迎關注微信公眾號:傅里葉變換,個人賬號,僅用于技術交流 
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/270596.html
標籤:Java
上一篇:HashMap底層原理分析
下一篇:進制及其轉換
