1. 程式人生 > >佇列同步器 AQS

佇列同步器 AQS

佇列同步器AbstractQueuedSynchronizer,是用來構建鎖或者其他同步元件的基礎框架,首先要知道這個共享變數的狀態(是否已經被其他執行緒鎖住等),這設定了一個int成員變量表示同步狀態,通過內建的先進先出佇列來完成資源獲取執行緒的排隊工作。一些執行緒無法獲取到共享資源等待獲取鎖而進入一個容器中進行儲存,而這容器就是一個先進先出佇列。

同步器的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態,在抽象方法的實現過程中免不了要對同步狀態(上文的int成員變數)進行修改,這時就需要使用同步器提供的3個方法(getState()、setState()、compareAndSetState()) 來進行操作,因為它們能夠保證狀態的改變是安全的。

下面是內部類Node原始碼:

static final class Node {
        /** 標識一個這個節點是否為shared型別 */
        static final Node SHARED = new Node();
        /** 表示節點在獨佔模式下等待的標記 */
        static final Node EXCLUSIVE = null;
 
        /** 等待狀態值表明執行緒已經取消*/
        static final int CANCELLED =  1;
        /** 等待狀態值來指示繼任者的執行緒需要取消等待 */
static final int SIGNAL = -1; /** 等待狀態值表明執行緒正在等待條件*/ static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ static final int PROPAGATE = -3; /** * 等待狀態值,又以下狀態值: * * SIGNAL: 值為-1 ,後續節點處於等待狀態,而當前節點的執行緒如果 * 釋放了同步狀態或者取消等待,節點進入該狀態不會變化 * CANCELLED: 值為 1,由於在同步佇列中等待的執行緒等待超時或者被中斷 * 需要從同步佇列中取消等待,節點進入該狀態將不會變化 * CONDITION: 值為-2,節點在等待佇列中,節點執行緒等待在Condition上, * 當其他執行緒對Condition呼叫了signal()方法後,該節點將會 * 從等待隊裡中轉移到同步佇列中,加入對同步狀態的獲取中 * PROPAGATE: 值為-3,表示下一次共享式同步狀態獲取將會無條件地被傳播下去 * * 0: 初始化狀態 */
volatile int waitStatus; /** * 前驅節點,當節點加入同步佇列時被設定 */ volatile Node prev; /** * 後繼節點 */ volatile Node next; /** * 獲取狀態狀態的執行緒 */ volatile Thread thread; /** * 等待佇列中的後繼節點。如果當前節點是共享的,那麼這個欄位是一個shared常量, * 也就是說節點型別(獨佔或共享)和等待佇列中個後繼節點共用同一個欄位 */ Node nextWaiter; /** * Returns true if node is waiting in shared mode. */ final boolean isShared() { return nextWaiter == SHARED; } /** * Returns previous node, or throws NullPointerException if null. * Use when predecessor cannot be null. The null check could * be elided, but is present to help the 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; } }

子類推薦被定義為自定義同步元件的靜態內部類,同步器自身沒有實現任何同步介面,它僅僅是定義了若干同步狀態獲取和釋放方法來供自定義同步元件使用,同步器既可以支援獨佔式地獲取同步狀態,也可以支援共享式地獲取同步狀態,這樣就可以方便實現不同型別的同步組(ReentranLock、ReentranReadWriteLock、CountDownLatch等)。
同步器是實現鎖(也可以是任意同步元件)的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義。可以這樣理解二者的關係:

(1) 鎖是面向使用者的,它定義了使用者與鎖互動的介面(比如可以允許兩個 執行緒並行訪問),隱藏了實現細節。
(2) 同步器面向的是鎖的使用者,它簡化了鎖的實現方式,遮蔽了同步狀態管理、執行緒的排隊、等待與喚醒等底層操作。或者可以把AQS認為是鎖的實現者的一個父類。

AbstractQueuedSynchronizer的主要方法:
大致將AbstractQueuedSynchronizer的方法分為以下幾種:
(1) Public final
(2) Protected final
(3) Private
(4) Protected

AbstractQueuedSynchronzier是一個抽象類,但是卻沒有一個抽象方法,但是主要的方法可分為上述的四種,我們知道final修飾的方法是不可以被子類重寫的,protected修飾的方法是可以被子類過載的,下邊展示一下大致分的四類方法。
1: Protected 類別
AbstractQueuedSynchornizer雖然沒有抽象方法,但是提供了五個方法可以讓我們在子類中過載,並且這五個方法都是空的實現且直接丟擲異常,也就是說我們要使用這五個方法所提供的功能,必須在子類中自己實現,這也是模板方法模式的一種體現和使用。這五個方法的具體含義如下:
(1) Boolean tryAcquire(int arg):獨佔式獲取同步狀態,實現該方法需要查詢當前狀態並判斷同步狀態是否符合預期,然後再進行CAS設定同步狀態
(2) Boolean tryRelease(int arg):獨佔式釋放同步狀態,等待獲取同步狀態的執行緒將有機會獲取同步狀態
(3) Int tryAcquireShared(int arg):共享式獲取同步狀態,返回大於等於0的值,表示獲取成功,反之獲取失敗
(4) Boolean tryReleaseShared(int arg) 共享式釋放同步狀態
(5) Boolean is HeldExclusively():當前同步器是否在獨佔模式下被執行緒佔用,一般該方法表示是否被當前執行緒鎖獨佔。

2:Public final 類別

除了上述protected的方法之外,還有一個關鍵的類別就是public final,這是我們可以直接使用的方法,稱之為模板方法,當我們實現自定義的同步元件的時候,我們可以呼叫這些模板方法獲取我們需要的東西。
(1) Void acquire(int arg) 獨佔式獲取同步狀態,如果當前執行緒獲取同步狀態成功,則由該方法返回,否則,將進入同步佇列等待,該方法將會呼叫重寫的tryAcquire(int arg) 方法
(2) Void acquireInterruptibly(int arg) 與acquire相同,但是該方法會響應中斷,當前執行緒未獲取到同步狀態而進入同步佇列中,如果當前執行緒被中斷,則該方法會丟擲InteruptedException並返回
(3) Boolean tryAcquireNanos(int arg,long nanos) 在acquireInterruptibly(int arg )的基礎上增加了超時限制 如果當前執行緒在超時時間內沒有獲取到同步狀態,那麼將會返回false,如果獲取到了返回為true
(4) Void acquireShared(int arg) 共享式的獲取同步狀態,如果當前執行緒為獲取到同步狀態,將會進入同步佇列等待,與獨佔式獲取的主要區別是在同一時刻可以有多個執行緒獲取到同步狀態。
(5) Void acquireSharedInterruptibly(int args) 與acquireShared(int arg)相同,該方法響應中斷
(6) boolean tryAcquireSharedNanos(int arg,long nanos) 在acquireShared(int arg) 基礎上增加了超時限制
(7) boolean release(int arg) 獨佔式的釋放同步狀態,該方法會在釋放同步狀態之後,將同步佇列
(8) boolean releaseShared(int arg) 共享式的釋放同步狀態
(9) Collection< Thread >getQueuedThreads() 獲取等待在同步佇列上的執行緒集合。

同步器提供的上述模板方法基本上分為3類:獨佔式獲取與釋放同步狀態、共享式獲取與釋放同步狀態、查詢同步佇列中的等待執行緒情況。
自定義同步元件將使用同步器提供的模板方法來實現自己的同步語義。只有掌握了同步器的工作原理才能更加深入地理解併發包中的其他元件。

3:protected final 類別
上文中,我們至少知道了我們要對int型別的同步狀態進行修改,下邊的三個方法提供了可以修改:
getState():int 獲取當前同步狀態
setState(int):void 設定當前同步狀態
compareAndSetState(int,int):boolean 使用CAS設定當前狀態,該方法能保證狀態設定的原子性。
另外還有三個:hasWaiters、getWaitQueueLength、getWaitingThreads三個方法
再看一下AbstractQueuedSynchronizer的內部類:
AbstractQueuedSynchornizer 有兩個內部類:一個是ConditionObject,另一個是Node。

ConditionObject內部類:
(1) ConditionObjecy
這個我們知道在使用synchronized的時候是使用wait和notify進行執行緒間通訊,使用ReentranLock的時候是使用Condition實現的執行緒通訊,而這正是AbstractQueuedSynchronizer幫我們進一步封裝的Condition介面:
(2) Condition介面如下:
(3) ConditionObject實現了Condition介面:
(4) 呼叫ReentranLock的newCondition方法正是返回的是ConditionObject物件