JDK 源碼分析之ReentrantLock

ReentrantLock 可重入鎖,實現Lock 接口。使用同步器實現加鎖解鎖,鎖等待。

  1. 加鎖過程如下:
  2. 代碼解析:
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    
    //同步器,繼承AQS
    private final Sync sync;
    
    /**
    * AQS 加鎖
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

    /**
     * Performs {@link Lock#lock}. The main reason for subclassing
     * is to allow fast path for nonfair version.
     */
    abstract void lock();

    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
     // 非公平模式嘗試獲取鎖
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //如果沒有線程持有鎖,即同步器的同步狀態state等於0
        if (c == 0) {
            //CAS 設置同步器狀態,成功則獲取鎖
            if (compareAndSetState(0, acquires)) {
                //同步器持有的排他線程設置爲當前線程
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //同步器持有的排他線程等於當前線程,則鎖重入。
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            //可重入次數不能大於int的最大值,否則則會異常。
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
                //獲取鎖成功,設置同步器 的同步狀態
            setState(nextc);
            return true;
        }
        //CAS設置失敗,獲取鎖失敗
        return false;
    }
    
    //嘗試釋放鎖
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        // 如果當前線程不等於同步器持有 的排他線程,則拋出異常。
        //這相當於當前線程試圖釋放不屬於自己鎖的線程,自然不能釋放
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        //同步器狀態減去傳來的參數爲0,則說明當前線程只獲得一次鎖。
        if (c == 0) {
            // 返回true即釋放鎖成功
            free = true;
            setExclusiveOwnerThread(null);
        }
        //如果c不等於0,則說明當前線程多次獲得鎖,即可重入獲取。返回false
        setState(c);
        return free;
    }

    //當前線程是否持有鎖
    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // Methods relayed from outer class
    // 獲取持有鎖的線程
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    //獲取當前線程持有鎖的計數
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     */
     //從流中重構實例
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

//  通過維護一個先進先出的隊列,實現鎖或同步器。

* <pre>
*      +------+  prev +-----+       +-----+
* head |      | <---- |     | <---- |     |  tail
*      +------+       +-----+       +-----+
* </pre>

加鎖過程:
// 1.調用內部類NonfairSync的lock方法   默認非公平鎖的模式
public void lock() {
    sync.lock();
}

// 繼承sync,實現非公平鎖。實現方式爲來一個線程調用lock方法時,
// 就讓這個線程先嚐試去獲取鎖,獲取不到則加入等待隊列。
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
    
        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            // 2 嘗試設置同步狀態值爲1,0代表state的狀態沒有線程持有鎖,也沒有等待獲取鎖的隊列
            // 即當前線程是第一個嘗試獲取鎖的。設置成功則將當前線程持有該排它鎖。
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //3.否則嘗試獲取鎖,失敗則加入等待隊列以獨佔模式
                acquire(1);
        }
        // 嘗試獲取鎖 ,失敗則嘗試加入等待隊列
        public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //獲取鎖失敗且線程被中斷則調用線程中斷方法
            selfInterrupt();
        }
       // 4  非公平方式嘗試獲取鎖
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
    //5  以非公平的模式嘗試獲取鎖 此方法來自sync
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 獲取當前鎖對象狀態
        int c = getState();
        // 0 代表沒有任何線程獲得鎖
        if (c == 0) {
            // CAS嘗試設置state的值,成功則將當前線程設置爲獨佔模式下持有
            // 的線程。
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //判斷當前線程是否是持有獨佔鎖的線程,是則設置同步狀態。
        // 這裏就實現了鎖的可重入特性。同一個線程獲得鎖後再次獲取鎖。
        // 會將同步狀態加1
        else if (current == getExclusiveOwnerThread()) {
            // 設置下一個同步狀態值
            int nextc = c + acquires;
            //小於0 則棧溢出 ,如果一個線程很多次獲取同一個鎖,超過了鎖的最大數,則拋出異常
            //超過最大鎖數 ,int類型的最大值加1後等於int類型的最小值,負數。
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            // 設置同步狀態值    
            setState(nextc);
            return true;
        }
        return false;
    }


    // // 6 上一步獲取失敗後,當前線程加入等待隊列
    private Node addWaiter(Node mode) {
        // 獨佔模式新創建一個持有當前線程的節點。
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        // 當前隊列尾部不爲空。
        if (pred != null) {
            // 當前節點的前進指針指向尾結點。
            // 即當前節點的上一個節點指向尾結點
            node.prev = pred;
            // CAS 嘗試 將新節點設置爲尾結點
            if (compareAndSetTail(pred, node)) {
                // 成功將當前尾結點的next指針指向新節點。返回新節點
                pred.next = node;
                return node;
            }
        }
        //對列尾結點爲空,使用enq嘗試加入節點
        enq(node);
        return node;
    }
    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
     // 初始化對列,並把node加入隊列尾部
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            //尾結點爲空,說明對列爲空,則初始化當前對列
            if (t == null) { // Must initialize
                // cas 嘗試設置一個頭結點,成功則將頭結點賦值給尾結點。繼續循環
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 當前節點的上一個節點設置爲當前的尾結點
                node.prev = t;
                // CAS嘗試將node 設置爲尾結點。成功
                // 則將之前尾結點的下一個節點設置爲node
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
     //加入等待隊列後,再次嘗試獲取鎖
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            // 循環獲取知道當前node節點的上一個節點爲頭結點。
            // 然後嘗試獲取鎖。這就相當於是自旋方式獲取鎖
            for (;;) {
                //獲取node 節點的上一個節點,爲空拋出異常
                final Node p = node.predecessor();
                //上一個節點爲頭結點則嘗試獲取鎖
                if (p == head && tryAcquire(arg)) {
                    //獲取成功則將node 節點設置爲頭結點,頭結點
                    //頭結點的thread爲null,prev也是null。
                    // 所以node沒有指向p的引用了。
                    setHead(node);
                    // 之前頭結點即p的下一個節點指向null,那麼p沒有指向node的引用了,
                    // 那麼此時p節點的狀態就是一個獨立的對象,沒有任何對象有引用指向p,
                    //垃圾回收的可達性算法說明該節點可以回收了。
                    //
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                
                // 嘗試設置等待狀態爲-1,爲-1則檢查當前線程是否中斷。
                // 中斷成功設置中斷標識爲true.  線程阻塞成功且已經中斷
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    // 獲取失敗後當前線程是否應該阻塞
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // -1 代表可以阻塞該線程。
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
             //循環對列獲取一個不大於0 的節點
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
             // 等於0 或小於0 不等於-1 則CAS嘗試將節點等待狀態改爲-1
             // 這樣下次可以阻塞該線程
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    // 調用park 阻塞當前線程
    private final boolean parkAndCheckInterrupt() {
        //阻塞當前線程
        LockSupport.park(this);
        //當前線程已經被中斷返回true. 未中斷 false
        return Thread.interrupted();
    }


//  解鎖過程
// 1 。調用unlock
public void unlock() {
    sync.release(1);
}

// 解鎖
public final boolean release(int arg) {
    // 嘗試解鎖,如果當前線程不是持有鎖的線程拋出異常
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
    // 
}
// 嘗試解鎖 
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 如果當前線程不是持有鎖的線程拋出異常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 狀態爲0,說明該線程不再持有鎖
    // 不等於0,則說明該線程重複持有鎖,需要多次解鎖直到狀態爲0則不再持有鎖。
    // 如果一個線程多次持有一個鎖,會造成阻塞。
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //設置狀態
    setState(c);
    return free;
}
// 嘗試喚醒下一個等待鎖的線程
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    // 當前節點等待狀態小於0,則設置等待狀態爲0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    // 下一個節點爲空,等待狀態大於0 
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 循環找到一個狀態小於等於0的最靠近頭結點的節點
        // 且該節點不等於當前節點
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 下一個節點不爲空,則喚醒阻塞的節點
    if (s != null)
        LockSupport.unpark(s.thread);
}
// 鎖釋放過程: 嘗試釋放線程持有的鎖,狀態減一,減一後狀態爲0 ,則設置當前鎖對象持有的線程爲空。
// 鎖對象狀態變爲0,返回true。
// 鎖釋放成功後,檢查頭結點不爲空且等待狀態不等於0,
// 則 嘗試喚醒下一個節點,則下一個節點可以嘗試獲取鎖。 
//  

}

 

  •   這樣一個完整的加鎖過程。總結來看,

  •   如果獲取鎖的時候,state 狀態爲0 , CAS 設置state狀態變爲1 成功,則當前線程獲得鎖。cas設置失敗,則再次嘗試獲取鎖。

  • 獲取當前 state狀態爲0,則再次嘗試CAS設置狀態,成功則將當前線程設置爲獨佔線程,獲得鎖。

  •  不等於0 則判斷當前線程是否爲持有鎖的線程,是,則可以再次獲得鎖。將state狀態加1. 加1後小於0,則拋出超出鎖數量超出異常。設置狀態後返回true,則當前線程獲得鎖繼續執行。

  •  獲取鎖失敗,則以獨佔模式加入等待隊列。如果尾結點存在則當前節點插入作爲尾結點。 不存在則使用enq初始化隊列,new一個空節點作爲頭結點也是尾結點。 循環,再次將當前節點設爲尾結點,返回成功。

  • 然後如果當前節點的上一個節點時頭結點,則嘗試獲取鎖。

  • 檢查線程狀態,查看線程是否應該被阻塞且中斷標誌是否爲true。 如果滿足兩個條件,則獲取鎖成功後會調用線程的中斷方法中斷當前線程

  •  即線程在循環等待獲取鎖的過程中可能被中斷了,中斷狀態爲true,此時線程沒有真正中斷。在獲取鎖成功後調用線程的中斷方法。

  • 如果沒有中斷則獲取鎖成功,繼續執行。

  • 特點:

  •  通過 state 同步狀態控制鎖的獲取,通過雙向隊列進行線程的排隊獲取鎖。

  •  通過 持有線程的引用實現鎖的可重入特性及鎖的準確釋放及誰獲取的鎖誰釋放。

  • 通過CAS 來設置同步狀態,頭節點,尾節點等。實現了一個輕量級的可重入鎖。

  • 通過嘗試加鎖的時候是否判斷有線程等待的比當前獲取鎖的線程時間長來實現公平性。

  • 加入等待隊列的尾插法也體現了公平性。因爲獲取鎖總是從頭節點開始。

發佈了122 篇原創文章 · 獲贊 28 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章