1. 程式人生 > >ReentrantLock原始碼之二unlock方法解析(鎖的釋放)

ReentrantLock原始碼之二unlock方法解析(鎖的釋放)

前面一節中,我們分析了ReentrantLock.lock()方法,接下來我們接著分析ReentrantLock.unlock()方法。

1.ReentrantLock.unlock()分析

(1)首先嚐試釋放鎖,如果要求釋放數等於鎖狀態數,那麼將鎖狀態位清0,清除鎖所有者,返回true;否則返回false;
(2)如果(1)返回的是true,說明鎖完全釋放。接下來將檢查等待佇列,並選擇下一個節點恢復(該節點必須是waitStatus),

如果該節點不存在或者被取消,則選擇從尾節點開始,選取最靠近頭節點的等待節點,同時清理佇列中執行緒被取消的節點;

大家回憶下lock()實現機制,新節點是從尾部插入的。這就說明AQS裡面的 CLH其實是一個FIFO

佇列(用雙鏈表實現的FIFO佇列).

(3)如果(1)返回false,說明鎖只是部分釋放,當前執行緒仍舊持有該鎖;

 1java.util.concurrent.locks.ReentrantLock
 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    }