干貨ReentrantLock非公平鎖源碼分析
本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲。轉載本文請聯系Java極客技術公眾號。
1.鎖
java中,加鎖的方式
- synchronized,這個是 java 底層實現的,也就是 C 語言實現的。
- . lock,這個是 java.util.concurrent 包下面的,是 java語言實現的。
2.ReentrantLock
ReentrantLock 是 Lock 的一種實現,是一種可重入的公平或非公平鎖。默認是非公平鎖。
2.1 Lock的創建
首先看下鎖的創建和使用代碼:
- //創建鎖
- Lock lock = new ReentrantLock();
- //加鎖
- lock.lock();
- //釋放鎖
- lock.unlock();
然后看下創建的是 ReentrantLock 的構造函數:
- public ReentrantLock() {
- sync = new NonfairSync();
- }
NonfairSync 就是非公平鎖。所以 ReentrantLock 默認是非公平鎖的實現
2.2 lock()
加鎖的邏輯就比較復雜了,因為存在線程競爭。所以有兩種情況,一種是競爭到鎖的處理,一種是沒有競爭到鎖的處理。
首先我們還是來看下 lock() 方法,因為最終是非公平的實現,所以直接看 NonfairSync 里面的 lock 方法。
- final void lock() {
- if (compareAndSetState(0, 1))
- setExclusiveOwnerThread(Thread.currentThread());
- else
- acquire(1);
- }
2.3 沒有獲取到鎖的邏輯 acquire()
直接上代碼:
- public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
- }
還是3個方法,阿粉一個一個的說。
tryAcquire(arg) ,還是先看代碼在分析。
- 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;
- }
a. 獲取 state ,如果等于0,說明之前獲得鎖的線程已經釋放了,那么這個線程就會再次去競爭鎖,這就是非公平鎖的體現,如果是公平鎖,是沒有這個判斷的。
b. 如果前一個獲得鎖的線程沒有釋放鎖,那么就判斷是否是同一個線程,是的話就會將 state 加 1。這個就是重入鎖的體現。
c. 如果都不滿足,那么返回 false。
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) ,再次獲取鎖沒有成功,并且又不是可重入鎖,那么就存入一個阻塞隊列里面。里面還有一點邏輯,就不展開了,有興趣可以自己看下。
selfInterrupt(); 這個是當前線程的中斷標志,作用就是在線程在阻塞的是否,客戶端通過調用了中斷線程的方法 interrupt(),那么該線程被喚醒的時候,就會有響應的處理。具體要看這個線程 run 方法里面的代碼邏輯。
2.4 unlock()
- protected final boolean tryRelease(int releases) {
- int c = getState() - releases;
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- free = true;
- setExclusiveOwnerThread(null);
- }
- setState(c);
- return free;
- }
state - 1,如果大于0,說明釋放的是重入鎖,只需要修改 state 就行了
如果等于0,說明要釋放鎖,釋放鎖首先需要把獨占線程設置為null,再把state設置為0。
3 總結
Lock 鎖的實現:
互斥性:需要一個狀態來判斷是否競爭到鎖:state 并且需要用 volatile修飾,保證線程之間的可見性。
可重入性:Thread exclusiveOwnerThread 這個成員變量來記錄當前獲得鎖的線程。
公平或非公平:默認非公平,NonfairSync。
沒有競爭到鎖的線程怎么辦?放到隊列中。
沒有競爭到鎖的線程怎么釋放CPU?park:阻塞線程釋放CPU資源,這個操作在 acquireQueued(),阿粉沒沒有講這個。
最后來張流程圖: