ReentrantLock加鎖解鎖過程


公平鎖

調用lock方法加鎖

 

進入acquire方法獲取加鎖的許可

 

 進入tryacquire

 

 首先獲取當前線程和status狀態,status默認為0

假如現在t1線程進入,然后t2線程進入(t2進入時t1還沒有釋放鎖)

if c==0成立,然后判斷是否需要排隊,調用hasqueuedpredecessors方法

 

 此時的頭和尾都是null,此方法返回false,所以上面if(!hasqueuedpredecessors())成立,然后進行cas操作,將status改為1

然后設置持有鎖的是當前線程。最后返回true。即t1拿到鎖繼續執行自己的業務邏輯。。。。

1、如果t2執行lock方法的時候t1已經釋放鎖,也就不會存在競爭,一次執行。

2、如果t1還沒有釋放鎖,t2也會走上面的代碼。

走到tryacquire方法嘗試去獲取鎖的時候肯定失敗,因為t1還在占用。方法返回false。

 

 然后會執行addwriter方法進入隊列排隊。

 

 首先創建一個節點node,節點里包含屬性thread,pre,next,是一個雙向鏈表

由於tail=null,所以會走enq方法。

 

 此處無限循環,t==null也成立,首先會先初始化一個新的Node,node里的信息目前為空。然后設置為頭部。(其實隊列里的頭部永遠都是一個空節點,空節點的意思是有node對象,只不過里面的thread屬性為空)

繼續循環,走else,里面的邏輯意思是將自己的節點設置為尾部,空節點設置為頭部,然后將空節點的next指向t2節點,t2的pre指向頭部的空節點。

最終的關系如下圖

 

 

繼續走acquirequeued方法

 

 

 1 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 2         int ws = pred.waitStatus;//等待狀態,默認為0,Node.SIGNAL=-1
 3         if (ws == Node.SIGNAL)
 4             /*
 5              * This node has already set status asking a release
 6              * to signal it, so it can safely park.
 7              */
 8             return true;
 9         if (ws > 0) {
10             /*
11              * Predecessor was cancelled. Skip over predecessors and
12              * indicate retry.
13              */
14             do {
15                 node.prev = pred = pred.prev;
16             } while (pred.waitStatus > 0);
17             pred.next = node;
18         } else {
19             /*
20              * waitStatus must be 0 or PROPAGATE.  Indicate that we
21              * need a signal, but don't park yet.  Caller will need to
22              * retry to make sure it cannot acquire before parking.
23              */
24             compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
25         }
26         return false;
27     }

所以會走到compareAndSetWaitStatus方法將上一個節點的waitstatus改為-1,然后返回false,由於acquirequeued方法里是for(;;)

所以會繼續執行shouldParkAfterFailedAcquire,此時的waitstatus=-1,返回true。

這個時候就是所謂的自旋,自旋了兩次。

返回true之后根據上面代碼的邏輯會走parkandcheckinterrupt方法

 

 

 調用park方法,此時t2開始阻塞。知道t1釋放鎖。

此時t3如果也進入lock方法。

如果t1還沒有釋放鎖, 和t2同樣的邏輯。直到t3也阻塞。

如果此時t1已經釋放鎖,那么會是t2拿到鎖。因為入隊之后,會嘗試一次加鎖許可的過程中,會判斷當前節點是不是第一個排隊的節點(也就是他的上一個節點是不是頭部)

如果是才有資格去獲取鎖,因為前面還有t2,所以t3會繼續排隊,t2被喚醒之后,會將自己節點的thread屬性賦值null,next指向t3,t3的pre指向t2。

然后把頭部指向t2,尾部指向t3。空節點的next=null。

此時最先初始化的那個空節點已經沒有任何引用。等待被回收。源碼里有體現。

非公平鎖

 

 調用lock方法,直接就就行cas操作。

unlock解鎖過程

 

 

 

 

 

 

 

 

 簡單幾步:1、將status設置為0

2、設置持有當前鎖的線程為null

 

3、喚醒隊列里waitstatus不等於0的節點,調用unpark方法。

 

 

 簡單總結:調用lock方法,存在競爭的時候,T2會去入隊,首先會初始化一個空節點,t2節點實際上存放的是第二個位置,t3進來的時候繼續在后面排隊,

 t2和t3都是調用park方法進行阻塞。入隊的時候會將前面的節點的waitstatus狀態由0改為-1。在調用unlock的時候會將waitstatus不等於0的釋放。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM