ReentrantLock原始碼之二unlock方法解析(鎖的釋放)
阿新 • • 發佈:2019-01-23
在前面一節中,我們分析了ReentrantLock.lock()方法,接下來我們接著分析ReentrantLock.unlock()方法。
佇列(用雙鏈表實現的FIFO佇列).
2 public void unlock() {
3 sync.release(1);
4 }
5
6java.util.concurrent.locks.AbstractQueuedSynchronizer
7public final boolean release(int arg) {
8 if (tryRelease(arg)) {
9 Node h = head;
10 if (h != null && h.waitStatus != 0)
11 unparkSuccessor(h);
12 return true;
13 }
14 return false;
15 }
16
17
18 protected final boolean tryRelease(int releases) {
19 int c = getState() - releases; //重入鎖加鎖的次數-釋放數量20 if (Thread.currentThread() != getExclusiveOwnerThread()) // 判斷獨佔鎖是否為當前執行緒所有21 throw new IllegalMonitorStateException();
22 boolean free = false;
23 if (c == 0) { //加鎖次數=釋放數量24 free = true;
25 setExclusiveOwnerThread(null); //清除鎖擁有者標識26 }
27 setState(c); //設定加鎖狀態28 return free;
29 }
30
31
32 /**33 * Wakes up node's successor, if one exists.
34 *
35 * @param node the node
36 */
37 private void unparkSuccessor(Node node) {
38 /*39 * Try to clear status in anticipation of signalling. It is
40 * OK if this fails or if status is changed by waiting thread.
41 */
42 compareAndSetWaitStatus(node, Node.SIGNAL, 0); //清除頭節點signal狀態43
44 /*45 * Thread to unpark is held in successor, which is normally
46 * just the next node. But if cancelled or apparently null,
47 * traverse backwards from tail to find the actual
48 * non-cancelled successor.
49 */
50 Node s = node.next;
51 if (s == null || s.waitStatus > 0) { //等待佇列喚醒的競爭滿足FIFO,本段程式碼主要是尋找最靠近頭節點的,且waitStatus為signal、condition的連結串列節點52 s = null;
53 for (Node t = tail; t != null && t != node; t = t.prev)
54 if (t.waitStatus <= 0)
55 s = t;
56 }
57 if (s != null)
58 LockSupport.unpark(s.thread);
59 }
1.ReentrantLock.unlock()分析
(1)首先嚐試釋放鎖,如果要求釋放數等於鎖狀態數,那麼將鎖狀態位清0,清除鎖所有者,返回true;否則返回false;
(2)如果(1)返回的是true,說明鎖完全釋放。接下來將檢查等待佇列,並選擇下一個節點恢復(該節點必須是waitStatus),
如果該節點不存在或者被取消,則選擇從尾節點開始,選取最靠近頭節點的等待節點,同時清理佇列中執行緒被取消的節點;
大家回憶下lock()實現機制,新節點是從尾部插入的。這就說明AQS裡面的 CLH其實是一個FIFO
(3)如果(1)返回false,說明鎖只是部分釋放,當前執行緒仍舊持有該鎖;
1java.util.concurrent.locks.ReentrantLock2 public void unlock() {
3 sync.release(1);
4 }
5
6java.util.concurrent.locks.AbstractQueuedSynchronizer
7public final boolean release(int arg) {
8 if (tryRelease(arg)) {
9 Node h = head;
10
11 unparkSuccessor(h);
12 return true;
13 }
14 return false;
15 }
16
17
18 protected final boolean tryRelease(int releases) {
19 int c = getState() - releases; //重入鎖加鎖的次數-釋放數量20 if (Thread.currentThread() != getExclusiveOwnerThread()) //
22 boolean free = false;
23 if (c == 0) { //加鎖次數=釋放數量24 free = true;
25 setExclusiveOwnerThread(null); //清除鎖擁有者標識26 }
27 setState(c); //設定加鎖狀態28 return free;
29 }
30
31
32 /**33 * Wakes up node's successor, if one exists.
34 *
35 * @param node the node
36 */
37 private void unparkSuccessor(Node node) {
38 /*39 * Try to clear status in anticipation of signalling. It is
40 * OK if this fails or if status is changed by waiting thread.
41 */
42 compareAndSetWaitStatus(node, Node.SIGNAL, 0); //清除頭節點signal狀態43
44 /*45 * Thread to unpark is held in successor, which is normally
46 * just the next node. But if cancelled or apparently null,
47 * traverse backwards from tail to find the actual
48 * non-cancelled successor.
49 */
50 Node s = node.next;
51 if (s == null || s.waitStatus > 0) { //等待佇列喚醒的競爭滿足FIFO,本段程式碼主要是尋找最靠近頭節點的,且waitStatus為signal、condition的連結串列節點52 s = null;
53 for (Node t = tail; t != null && t != node; t = t.prev)
54 if (t.waitStatus <= 0)
55 s = t;
56 }
57 if (s != null)
58 LockSupport.unpark(s.thread);
59 }