Java併發學習筆記13 AQS 概覽

bilibili-Java併發學習筆記13 AQS 概覽

基於 java 1.8.0

P45_AQS整體架構與設計原則剖析

AbstractQueuedSynchronizer 源碼

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import sun.misc.Unsafe;

/**
 * 爲實現依賴於先進先出 (FIFO) 等待隊列的阻塞鎖和相關同步器(信號量、事件,等等)提供一個框架。
 * 此類的設計目標是成爲依靠單個原子 int 值來表示狀態的大多數同步器的一個有用基礎。
 * 子類必須定義更改此狀態的受保護方法,並定義哪種狀態對於此對象意味着被獲取或被釋放。
 * 假定這些條件之後,此類中的其他方法就可以實現所有排隊和阻塞機制。
 * 子類可以維護其他狀態字段,但只是爲了獲得同步而只追蹤使用 getState()、setState(int)
 * 和 compareAndSetState(int, int) 方法來操作以原子方式更新的 int 值。
 *
 * 應該將子類定義爲非公共內部幫助器類,可用它們來實現其封閉類的同步屬性。
 * 類 AbstractQueuedSynchronizer 沒有實現任何同步接口。
 * 而是定義了諸如 acquireInterruptibly(int) 之類的一些方法,
 * 在適當的時候可以通過具體的鎖和相關同步器來調用它們,以實現其公共方法。
 *
 * 此類支持默認的獨佔模式和共享模式之一,或者二者都支持。
 * 處於獨佔模式下時,其他線程試圖獲取該鎖將無法取得成功。
 * 在共享模式下,多個線程獲取某個鎖可能(但不是一定)會獲得成功。
 * 此類並不“瞭解”這些不同,除了機械地意識到當在共享模式下成功獲取某一鎖時,下一個等待線程(如果存在)也必須確定自己是否可以成功獲取該鎖。
 * 處於不同模式下的等待線程可以共享相同的 FIFO 隊列。通常,實現子類只支持其中一種模式,但兩種模式都可以在(例如)ReadWriteLock 中發揮作用。
 * 只支持獨佔模式或者只支持共享模式的子類不必定義支持未使用模式的方法。
 *
 * 此類通過支持獨佔模式的子類定義了一個嵌套的 AbstractQueuedSynchronizer.ConditionObject 類,可以將這個類用作 Condition 實現。
 * isHeldExclusively() 方法將報告同步對於當前線程是否是獨佔的;
 * 使用當前 getState() 值調用 release(int) 方法則可以完全釋放此對象;
 * 如果給定保存的狀態值,那麼 acquire(int) 方法可以將此對象最終恢復爲它以前獲取的狀態。
 * 沒有別的 AbstractQueuedSynchronizer 方法創建這樣的條件,因此,如果無法滿足此約束,則不要使用它。
 * AbstractQueuedSynchronizer.ConditionObject 的行爲當然取決於其同步器實現的語義。
 *
 * 此類爲內部隊列提供了檢查、檢測和監視方法,還爲 condition 對象提供了類似方法。
 * 可以根據需要使用用於其同步機制的 AbstractQueuedSynchronizer 將這些方法導出到類中。
 *
 * 此類的序列化只存儲維護狀態的基礎原子整數,因此已序列化的對象擁有空的線程隊列。
 * 需要可序列化的典型子類將定義一個 readObject 方法,該方法在反序列化時將此對象恢復到某個已知初始狀態。
 *
 * 使用
 *
 * 爲了將此類用作同步器的基礎,需要適當地重新定義以下方法,
 * 這是通過使用 getState()、setState(int) 和/或 compareAndSetState(int, int) 方法來檢查和/或修改同步狀態來實現的:
 *
 * tryAcquire
 * tryRelease
 * tryAcquireShared
 * tryReleaseShared
 * isHeldExclusively
 *
 * 默認情況下,每個方法都拋出 UnsupportedOperationException。
 * 這些方法的實現在內部必須是線程安全的,通常應該很短並且不被阻塞。
 * 定義這些方法是使用此類的‘唯一’受支持的方式。其他所有方法都被聲明爲 final,因爲它們無法是各不相同的。
 *
 * 您也可以查找從 AbstractOwnableSynchronizer 繼承的方法,用於跟蹤擁有獨佔同步器的線程。
 * 鼓勵使用這些方法,這允許監控和診斷工具來幫助用戶確定哪個線程保持鎖。
 *
 * 即使此類基於內部的某個 FIFO 隊列,它也無法強行實施 FIFO 獲取策略。獨佔同步的核心採用以下形式:
 *
 * Acquire:
 *     while (!tryAcquire(arg)) {
 *        enqueue thread if it is not already queued;
 *        possibly block current thread;
 *     }
 *
 * Release:
 *     if (tryRelease(arg))
 *        unblock the first queued thread;
 *
 * (共享模式與此類似,但可能涉及級聯信號。.)
 *
 * 因爲要在加入隊列之前檢查線程的獲取狀況,所以新獲取的線程可能闖入其他被阻塞的和已加入隊列的線程之前。
 * 不過如果需要,可以內部調用一個或多個檢查方法,通過定義 tryAcquire 和/或 tryAcquireShared 來禁用闖入。
 * 特別是 getFirstQueuedThread() 沒有返回當前線程的時候,嚴格的 FIFO 鎖定可以定義 tryAcquire 立即返回 false。
 * 只有 hasQueuedThreads() 返回 true 並且 getFirstQueuedThread 不是當前線程時,更好的非嚴格公平的版本纔可能會立即返回 false;
 * 如果 getFirstQueuedThread 不爲 null 並且不是當前線程,則產生的結果相同。出現進一步的變體也是有可能的。
 *
 * 對於默認闖入(也稱爲 greedy、renouncement 和 convoy-avoidance)策略,吞吐量和可伸縮性通常是最高的。
 * 儘管無法保證這是公平的或是無偏向的,但允許更早加入隊列的線程先於更遲加入隊列的線程再次爭用資源,並且相對於傳入的線程,每個參與再爭用的線程都有平等的成功機會。
 * 此外,儘管從一般意義上說,獲取並非“自旋”,它們可以在阻塞之前對用其他計算所使用的 tryAcquire 執行多次調用。
 * 在只保持獨佔同步時,這爲自旋提供了最大的好處,但不是這種情況時,也不會帶來最大的負擔。
 * 如果需要這樣做,那麼可以使用“快速路徑”檢查來先行調用 acquire 方法,以這種方式擴充這一點,
 * 如果可能不需要爭用同步器,則只能通過預先檢查 hasContended() 和/或 hasQueuedThreads() 來確認這一點。
 *
 * 通過特殊化其同步器的使用範圍,此類爲部分同步化提供了一個有效且可伸縮的基礎,
 * 同步器可以依賴於 int 型的 state、acquire 和 release 參數,以及一個內部的 FIFO 等待隊列。
 * 這些還不夠的時候,可以使用 atomic 類、自己的定製 Queue 類和 LockSupport 阻塞支持,從更低級別構建同步器。
 *
 * 使用示例
 *
 * 以下是一個非再進入的互斥鎖類,它使用值 0 表示未鎖定狀態,使用 1 表示鎖定狀態。
 * 當非重入鎖定不嚴格地需要當前擁有者線程的記錄時,此類使得使用監視器更加方便。
 * 它還支持一些條件並公開了一個檢測方法:
 *
 * class Mutex implements Lock, java.io.Serializable {
 *
 *   // Our internal helper class
 *   private static class Sync extends AbstractQueuedSynchronizer {
 *     // Reports whether in locked state
 *     protected boolean isHeldExclusively() {
 *       return getState() == 1;
 *     }
 *
 *     // Acquires the lock if state is zero
 *     public boolean tryAcquire(int acquires) {
 *       assert acquires == 1; // Otherwise unused
 *       if (compareAndSetState(0, 1)) {
 *         setExclusiveOwnerThread(Thread.currentThread());
 *         return true;
 *       }
 *       return false;
 *     }
 *
 *     // Releases the lock by setting state to zero
 *     protected boolean tryRelease(int releases) {
 *       assert releases == 1; // Otherwise unused
 *       if (getState() == 0) throw new IllegalMonitorStateException();
 *       setExclusiveOwnerThread(null);
 *       setState(0);
 *       return true;
 *     }
 *
 *     // Provides a Condition
 *     Condition newCondition() { return new ConditionObject(); }
 *
 *     // Deserializes properly
 *     private void readObject(ObjectInputStream s)
 *         throws IOException, ClassNotFoundException {
 *       s.defaultReadObject();
 *       setState(0); // reset to unlocked state
 *     }
 *   }
 *
 *   // The sync object does all the hard work. We just forward to it.
 *   private final Sync sync = new Sync();
 *
 *   public void lock()                { sync.acquire(1); }
 *   public boolean tryLock()          { return sync.tryAcquire(1); }
 *   public void unlock()              { sync.release(1); }
 *   public Condition newCondition()   { return sync.newCondition(); }
 *   public boolean isLocked()         { return sync.isHeldExclusively(); }
 *   public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
 *   public void lockInterruptibly() throws InterruptedException {
 *     sync.acquireInterruptibly(1);
 *   }
 *   public boolean tryLock(long timeout, TimeUnit unit)
 *       throws InterruptedException {
 *     return sync.tryAcquireNanos(1, unit.toNanos(timeout));
 *   }
 * }}
 *
 * 以下是一個鎖存器類,它類似於 CountDownLatch,除了只需要觸發單個 signal 之外。
 * 因爲鎖存器是非獨佔的,所以它使用 shared 的獲取和釋放方法。
 *
 * class BooleanLatch {
 *
 *   private static class Sync extends AbstractQueuedSynchronizer {
 *     boolean isSignalled() { return getState() != 0; }
 *
 *     protected int tryAcquireShared(int ignore) {
 *       return isSignalled() ? 1 : -1;
 *     }
 *
 *     protected boolean tryReleaseShared(int ignore) {
 *       setState(1);
 *       return true;
 *     }
 *   }
 *
 *   private final Sync sync = new Sync();
 *   public boolean isSignalled() { return sync.isSignalled(); }
 *   public void signal()         { sync.releaseShared(1); }
 *   public void await() throws InterruptedException {
 *     sync.acquireSharedInterruptibly(1);
 *   }
 * }}
 *
 * @since 1.5
 * @author Doug Lea
 */
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private static final long serialVersionUID = 7373984972572414691L;

    /**
     * 創建具有初始同步狀態 0 的新 AbstractQueuedSynchronizer 實例
     */
    protected AbstractQueuedSynchronizer() { }

    /**
     * 等待隊列 node 類.
     *
     * 等待隊列是"CLH"(Craig, Landin, and Hagersten)鎖隊列的變體。CLH 鎖通常用於自旋鎖。
     * 相反,我們使用它們來阻止同步器,但使用相同的基本策略,在其節點的前身中存儲有關線程的一些控制信息。
     * 每個節點中的 "status" 字段跟蹤線程是否應該阻止。
     * 節點在其前置任務釋放時被通知。隊列的每個節點都充當一個特定的通知樣式監視器,其中包含一個等待線程。
     * 但是,status字段不控制線程是否被授予鎖等。如果線程是隊列中的第一個線程,它可能會嘗試獲取。但做第一併不能保證成功;
     * 它只給了爭辯的權利。所以當前發佈的競爭者線程可能需要重新等待。
     *  
     * 爲了排隊進入一個CLH鎖,你需要把它作爲一個新的尾部進行原子拼接。要出列,只需設置head字段。
     *
     *      +------+  prev +-----+       +-----+
     * head |      | <---- |     | <---- |     |  tail
     *      +------+       +-----+       +-----+
     *
     * 插入 CLH 隊列只需要在“tail”上執行單個原子操作,因此有一個從 unqueued 到 queued 的簡單原子劃分點。
     * 類似地,出列只涉及更新“頭部”。然而,節點需要更多的工作來確定誰是它們的繼承者,部分地處理由於超時和中斷而可能發生的取消。
     *
     * “prev”鏈接(在最初的CLH鎖中沒有使用),主要用於處理取消。如果節點被取消,其後續節點(通常)將重新鏈接到未取消的前置節點。
     * 有關自旋鎖的類似機制的解釋,請參閱Scott和Scherer的論文
     * http://www.cs.rochester.edu/u/scott/synchronization/
     *
     * 我們還使用“next”鏈接來實現阻塞機制。
     * 每個節點的線程id都保存在它自己的節點中,因此前置節點通過遍歷下一個鏈接來確定它是哪個線程來通知下一個節點喚醒。
     * 確定後繼者必須避免與新排隊的節點爭用,以設置其前置節點的“下一個”字段。
     * 當一個節點的後繼者顯示爲空時,可以通過從原子更新的“tail”向後檢查來解決這個問題。
     * (或者,換言之,下一個鏈接是一個優化,這樣我們通常不需要反向掃描。)
     * 對消在基本算法中引入了一些保守性。
     * 因爲我們必須輪詢其他節點的取消,所以我們可能忽略了被取消的節點是在我們前面還是後面。
     * 處理這一問題的辦法是,在取消繼承人時,總是不加區分,讓他們穩定在一個新的前任身上,除非我們能確定一個未被取消的前任將承擔這一責任。
     * 
     * CLH隊列需要一個虛擬頭節點才能開始。但我們不會在建築上創造它們,因爲如果沒有爭論的話,那將是浪費精力。
     * 相反,在第一次爭用時構造節點並設置頭和尾指針。等待條件的線程使用相同的節點,但使用附加鏈接。
     * 條件只需要鏈接簡單(非併發)鏈接隊列中的節點,因爲它們只有在獨佔保留時才被訪問。等待時,將節點插入條件隊列。
     * 收到信號後,節點被轉移到主隊列。status字段的一個特殊值用於標記節點所在的隊列。
     *
     * 感謝Dave Dice、Mark Moir、Victor Luchangco、Bill Scherer和Michael Scott,以及JSR-166專家組的成員,感謝他們對本課程設計的有益想法、討論和評論。
     */
    static final class Node {
        /** 表示節點正在共享模式下等待的標記 */
        static final Node SHARED = new Node();
        /** 標記以指示節點正在獨佔模式下等待 */
        static final Node EXCLUSIVE = null;

        /** waitStatus值,指示線程已取消 */
        static final int CANCELLED =  1;
        /** waitStatus值,指示後續線程需要斷開連接 */
        static final int SIGNAL    = -1;
        /** waitStatus值,指示線程正在等待條件 */
        static final int CONDITION = -2;
        /**
         *  waitStatus值,指示下一個acquireShared應無條件傳播
         */
        static final int PROPAGATE = -3;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        volatile int waitStatus;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
        volatile Node prev;

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        Node nextWaiter;

        /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回上一個節點,如果爲null,則拋出NullPointerException。當前置任務不能爲空時使用。可以省略null檢查,但它的存在是爲了幫助VM。
         *
         * @return the predecessor of this node
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

    /**
     * 等待隊列的頭,延遲初始化。除了初始化,它只能通過方法setHead進行修改。注意:如果head存在,它的waitStatus保證不會被取消。
     */
    private transient volatile Node head;

    /**
     * 等待隊列的尾部,延遲初始化。僅通過方法enq修改以添加新的等待節點。
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;

    /**
     * 返回同步狀態的當前值。此操作具有 volatile 讀的內存語義。
     */
    protected final int getState() {
        return state;
    }

    /**
     * 設置同步狀態的值。此操作具有 volatile 寫的內存語義。
     */
    protected final void setState(int newState) {
        state = newState;
    }

    // Main exported methods

    /**
     * 試圖在獨佔模式下獲取對象狀態。此方法應該查詢是否允許它在獨佔模式下獲取對象狀態,如果允許,則獲取它。
     *
     * 此方法總是由執行 acquire 的線程來調用。
     * 如果此方法報告失敗,則 acquire 方法可以將線程加入隊列(如果還沒有將它加入隊列),直到獲得其他某個線程釋放了該線程的信號。
     * 可以用此方法來實現 Lock.tryLock() 方法。
     *
     * 默認實現將拋出 UnsupportedOperationException。
     *
     * @param arg acquire 參數。該值總是傳遞給 acquire 方法的那個值,或者是因某個條件等待而保存在條目上的值。該值是不間斷的,並且可以表示任何內容。
     * @return 如果成功,則返回 true。在成功的時候,此對象已經被獲取。
     * @throws IllegalMonitorStateException 如果正在進行的獲取操作將在非法狀態下放置此同步器。必須以一致的方式拋出此異常,以便同步正確運行。
     * @throws UnsupportedOperationException 如果不支持獨佔模式
     */
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * 試圖設置狀態來反映獨佔模式下的一個釋放。
     *
     * 此方法總是由正在執行釋放的線程調用。
     *
     * 默認實現將拋出 UnsupportedOperationException。
     *
     * @param arg release 參數。該值總是傳遞給 release 方法的那個值,或者是因某個條件等待而保存在條目上的當前狀態值。該值是不間斷的,並且可以表示任何內容。
     * @return 如果此對象現在處於完全釋放狀態,從而使等待的線程都可以試圖獲得此對象,則返回 true;否則返回 false。
     * @throws IllegalMonitorStateException 如果正在進行的釋放操作將在非法狀態下放置此同步器。必須以一致的方式拋出此異常,以便同步正確運行。
     * @throws UnsupportedOperationException 如果不支持獨佔模式
     */
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * 試圖在共享模式下獲取對象狀態。此方法應該查詢是否允許它在共享模式下獲取對象狀態,如果允許,則獲取它。
     *
     * 此方法總是由執行 acquire 線程來調用。
     * 如果此方法報告失敗,則 acquire 方法可以將線程加入隊列(如果還沒有將它加入隊列),直到獲得其他某個線程釋放了該線程的信號。
     *
     * 默認實現將拋出 UnsupportedOperationException。
     *
     * @param arg acquire 參數。該值總是傳遞給 acquire 方法的那個值,或者是因某個條件等待而保存在條目上的值。該值是不間斷的,並且可以表示任何內容。
     * @return 在失敗時返回負值;如果共享模式下的獲取成功但其後續共享模式下的獲取不能成功,則返回 0;如果共享模式下的獲取成功並且其後續共享模式下的獲取可能夠成功,則返回正值,在這種情況下,後續等待線程必須檢查可用性。(對三種返回值的支持使得此方法可以在只是有時候以獨佔方式獲取對象的上下文中使用。)在成功的時候,此對象已被獲取。
     * @throws IllegalMonitorStateException 如果正在進行的獲取操作將在非法狀態下放置此同步器。必須以一致的方式拋出此異常,以便同步正確運行。
     * @throws UnsupportedOperationException 如果不支持共享模式
     */
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * 試圖設置狀態來反映共享模式下的一個釋放。
     *
     * 此方法總是由正在執行釋放的線程調用。
     *
     * 默認實現將拋出 UnsupportedOperationException。
     *
     * @param arg release 參數。該值總是傳遞給 release 方法的那個值,或者是因某個條件等待而保存在條目上的當前狀態值。該值是不間斷的,並且可以表示任何內容。
     * @return 如果此對象現在處於完全釋放狀態,從而使正在等待的線程都可以試圖獲得此對象,則返回 true;否則返回 false
     * @throws IllegalMonitorStateException 如果正在進行的釋放操作將在非法狀態下放置此同步器。必須以一致的方式拋出此異常,以便同步正確運行
     * @throws UnsupportedOperationException 如果不支持共享模式
     */
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * 如果對於當前(正調用的)線程,同步是以獨佔方式進行的,則返回 true。此方法是在每次調用非等待 AbstractQueuedSynchronizer.ConditionObject 方法時調用的。(等待方法則調用 release(int)。)
     *
     * 默認實現將拋出 UnsupportedOperationException。此方法只是 AbstractQueuedSynchronizer.ConditionObject 方法內進行內部調用,因此,如果不使用條件,則不需要定義它。
     *
     * @return 如果同步是以獨佔方式進行的,則返回true;其他情況則返回 false
     * @throws UnsupportedOperationException 如果不支持這些條件
     */
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }
    // ... ...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章