三、最後小結一下
一、概念
我們談到併發,就不得不談reentrantlock
鎖;而談到reentrantlock
鎖,不得不談一下abstractqueuedsynchronized(aqs)!類如其名,抽象的佇列式的同步器,aqs定義了一套多執行緒訪問共享資源的同步器框架,許多同步類實現都依賴於它,如常用的reentrantlock/semaphore/countdownlatch…。我們以reentrantlock
作為講解切入點。
二、reentrantlock
原始碼分析
reentrantlock把所有lock介面的操作都委派到乙個sync類上,該類繼承了abstractqueuedsynchronizer:
static
abstract
class
sync
extends
abstractqueuedsynchronizer
sync又有兩個子類(分為:公平鎖和非公平鎖兩類):final
static
class
nonfairsync
extends
sync
final
static
class
fairsync
extends
sync
顯然是為了支援公平鎖和非公平鎖而定義,預設情況下為非公平鎖,設定為公平鎖可直接在建構函式中傳入true即可。
先理一下reentrant.lock()方法的呼叫過程(預設非公平鎖):
首先我們主要先講一下reentrantlock
類中的lock()
方法和uplock()
方法都做了些什麼事情。
1、lock()方法原始碼解析
// 首先會進入reentrantlock中呼叫lock()方法,接著呼叫aqs中lock()方法(1
)、 public
void
lock()
// 對所持有資源執行緒加鎖(2
)、 final
void
lock()
// 獲取鎖資源(3
)、public
final
void
acquire
(int arg)
// 從上一步可以看出,會將當前執行緒進入等待狀態,後續將子執行緒執行完才會去釋放鎖資源(4
)、private node addwaiter
(node mode)
}enq
(node)
;// 否則進入enq()方法
return node;
}// 這個方法會解決兩個問題 1、尾節點為空 2、cas嘗試設定鎖失敗(5
)、private node enq
(final node node)
else}}
}// 從等待佇列中去獲取鎖資源(6
)、 final
boolean
acquirequeued
(final node node,
int arg)
// 嘗試獲取鎖失敗,會將當前執行緒掛起,設定狀態為signal,等待其他執行緒執行完,才會被喚醒if(
shouldparkafte***iledacquire
(p, node)
&&parkandcheckinterrupt()
) interrupted =
true;}
}finally
// 將嘗試獲取鎖失敗的執行緒掛起(7
)、 private
static
boolean
shouldparkafte***iledacquire
(node pred, node node)
while
(pred.waitstatus >0)
; pred.next = node;
}else
return
false;}
// 將當前執行緒阻塞並掛起(8
)、 private
final
boolean
parkandcheckinterrupt()
// 嘗試獲取鎖狀態(9
)、protected
final
boolean
tryacquire
(int acquires)
// 非公平鎖獲取鎖狀態(10
)、final
boolean
nonfairtryacquire
(int acquires)
}// 當出現同乙個持有鎖的執行緒,再次獲取鎖,就會進行鎖重入過程
else
if(current ==
getexclusiveownerthread()
)return
false
;// 否則獲取鎖狀態失敗,符合fifo規則
}
通過上面分析完lock()
方法以後,我們來看一下uplock()
方法中原始碼解析
2、uplock()方法原始碼解析
// 1、首先uplock會去呼叫aqs裡面的release()方法
public
void
unlock()
// 2、aqs中release()方法
public
final
boolean
release
(int arg)
return
false;}
// 3、tryrelease()方法在reentrantlock類中呼叫
protected
final
boolean
tryrelease
(int releases)
setstate
(c);
// 重新設定state狀態,在這裡也有可能發生鎖重入
return free;
}// 4、呼叫aqs中的unparksuccessor()方法
private
void
unparksuccessor
(node node)
if(s != null)
// 判斷當前節點的下乙個節點是否為空
locksupport.
unpark
(s.thread)
;// 若不為空,則喚醒該執行緒
}
在這裡通過對uplock()
原始碼分析可知:首先會判斷當前執行緒鎖持有者是否被釋放,若是state = 0
,表明鎖被釋放且獲取頭節點,判斷頭節點是否為空且等待狀態不為cacelled
,則會將頭節點中的後續節點喚醒,否則繼續會讓後續節點掛起狀態,等待被喚醒。
三、最後小結一下
1、reentrantlock
鎖中最主要的就是lock()
方法和uplock()
方法,通過對這兩個方法原始碼分析,你可以發現,在併發過程中它是怎麼去加鎖和釋放鎖的過程,最主要的就是為什麼加鎖過程還需要用到cas
方式去獲取鎖,若是不使用cas
獲取鎖,有會引發什麼問題,有沒有更好的解決方案?這個你們可以深入考慮一下。
2、通過原始碼分析中,我們接觸到了公平鎖與非公平鎖之間獲取鎖過程中有什麼不同?各自都有什麼優勢,哪這裡又引入了乙個問題,在你們的理解中什麼是公平鎖和非公平鎖?
3、其實,從reentrantlock
切入點分析過程中,並不是特別難,包括cas
獲取鎖和加鎖的過程以及引入公平鎖與非公平鎖之間的實現方式,各自的優勢體現在哪方面;主要我們還是要去理解去掌握它的有關實現方式,我們可以想一下有沒比這種方式更好的實現方案。
結語:後續會繼續講aqs
中的countdownlatch
原始碼解析~
AQS深入理解
獨佔鎖的釋放 release方法 獨佔鎖的獲取與釋放總結 能響應中斷以及超時等待 共享鎖aqs實現的三個功能 對同步狀態的管理 對阻塞執行緒進行排隊 等待通知等底層功能的實現 aqs通過頭尾指標管理同步佇列的,獲取鎖失敗的執行緒入隊,釋放鎖對同步佇列中的執行緒進行通知 當共享資源被某個執行緒占有,其...
Okhttp深入理解及原始碼解析
okhttpclient okhttpclient new okhttpclient 第一行 request request new request.builder url url build 第二行 call call okhttpclient.newcall request 第三行 1 建立 o...
AQS原始碼解析
公平鎖 fairsync 核心方法 public final void acquire int arg tryacquire arg 方法 protected final boolean tryacquire int acquires 重入鎖 state 1 else if current gete...