公平鎖
調用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的釋放。
