AbstractQueuedSynchronizer源碼解讀

?? AbstractQueuedSynchronizer(AQS),是 Java 并發(fā)包中,實現(xiàn)各種同步結(jié)構(gòu)和部分其他組成單元(如線程池中的 Worker)的基礎(chǔ)。AQS 內(nèi)部數(shù)據(jù)和方法,可以簡單拆分為:

  • 一個 volatile 的int成員表征狀態(tài)(state屬性),同時提供了 setState 和 getState 方法。這里之所以用int不用boolean是因為有場景并不是只能有一個線程獲取鎖, 比如CountDownLatch就允許多個線程獲取鎖。
    1: ReentrantLock: 用state變量記錄某個線程獲取獨(dú)占鎖的次數(shù),獲取鎖時+1,釋放鎖時-1,在獲取時會校驗線程是否可以獲取鎖。
    2:Semaphore: 用state變量作為計數(shù)器,只有在大于0時允許線程進(jìn)入。獲取鎖時-1,釋放鎖時+1。
    3: CountDownLatch: 用state變量作為計數(shù)器,在初始化時指定。只要state還大于0,獲取共享鎖會因為失敗而阻塞,直到計數(shù)器的值為0時,共享鎖才允許獲取,所有等待線程會被逐一喚醒。
  • 一個先入先出(FIFO)的等待線程隊列,以實現(xiàn)多線程間競爭和等待,這是 AQS 機(jī)制的核心之一。
  • 各種基于 CAS 的基礎(chǔ)操作方法,以及各種期望具體同步結(jié)構(gòu)去實現(xiàn)的 tryAcquire/tryRelease 方法。


    AbstractQueuedSynchronizer關(guān)鍵屬性

下面來看看acquire獲取鎖的部分源碼:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
//定義為final說明子類不能覆蓋這個實現(xiàn)過程, 之類只需要實現(xiàn)tryAcquire方法
    public final void acquire(int arg) {
//這里分別會調(diào)用tryAcquire方法用于獲取鎖,如果失敗,會調(diào)用addWaiter方法,將當(dāng)前線的Node加入到FIFO隊列里,通過acquireQueued判斷當(dāng)前節(jié)點(diǎn)是否為頭節(jié)點(diǎn),如果是則試圖獲取鎖, 獲取不成功會加入等待隊列, 并把線程掛起, 不是頭結(jié)點(diǎn)的話直接加入等待隊列并掛起線程。
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

//這個方法在AbstractQueuedSynchronizer里留給子類來實現(xiàn),
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

}

這里分別會調(diào)用tryAcquire方法用于獲取鎖,如果失敗,會調(diào)用addWaiter方法,將當(dāng)前線的Node加入到FIFO隊列里,通過acquireQueued判斷當(dāng)前節(jié)點(diǎn)是否為頭節(jié)點(diǎn),如果是則試圖獲取鎖, 獲取不成功會加入等待隊列, 并把線程掛起, 不是頭結(jié)點(diǎn)的話直接加入等待隊列并掛起線程。在AbstractQueuedSynchronizer里tryAcquire方法是留給子類來實現(xiàn)的,下面來看看ReentrantLock里的非公平鎖是如何實現(xiàn)這里的邏輯的。源碼如下:

abstract static class Sync extends AbstractQueuedSynchronizer {
      //非公平鎖最終最調(diào)用到當(dāng)前這個方法,傳入的acquires為1
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
         int c = getState();// 獲取當(dāng)前 AQS 內(nèi)部狀態(tài)量
          if (c == 0) { // 0 表示無人占有,則直接用 CAS 修改狀態(tài)位,
                if (compareAndSetState(0, acquires)) {// 不檢查排隊情況,直接爭搶
                      setExclusiveOwnerThread(current);  // 并設(shè)置當(dāng)前線程獨(dú)占鎖
                    return true;
                }
          } else if (current == getExclusiveOwnerThread()) { // 即使?fàn)顟B(tài)不是 0,也可能當(dāng)前線程是鎖持有者,因為這是重入鎖
                int nextc = c + acquires;
                  if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
    }
    return false;
}


}

ReentrantLock的公平鎖的實現(xiàn)邏輯如下:

   static final class FairSync extends Sync {
//公平鎖的實現(xiàn)邏輯
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();// 獲取當(dāng)前 AQS 內(nèi)部狀態(tài)量
            if (c == 0) {// 0 表示無人占有,
                if (!hasQueuedPredecessors() && //這里需要判斷隊列頭節(jié)點(diǎn)的的下一個節(jié)點(diǎn)為空。也就是說不允許線程進(jìn)行插隊。
                    compareAndSetState(0, acquires)) { //為空才會嘗試獲取鎖
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) { // 即使?fàn)顟B(tài)不是 0,也可能當(dāng)前線程是鎖持有者,因為這是重入鎖
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

接下來再分析 acquireQueued,如果前面的 tryAcquire 失敗,代表著鎖爭搶失敗,進(jìn)入排隊競爭階段。這里就是利用 FIFO 隊列,實現(xiàn)線程間對鎖的競爭的部分,算是AQS 的核心邏輯。源碼如下:

//這個方法是不允許子類重寫的
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor(); //獲取前一個節(jié)點(diǎn)
                if (p == head && tryAcquire(arg)) { / 如果前一個節(jié)點(diǎn)是頭結(jié)點(diǎn),則當(dāng)前節(jié)點(diǎn)是FIFO隊列里的第一個節(jié)點(diǎn),則會再次調(diào)用tryAcquire方法嘗試獲取鎖。
                    setHead(node); //成功,則設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn)
                    p.next = null; // help GC 清空前面節(jié)點(diǎn)對當(dāng)前節(jié)點(diǎn)的引用
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && //獲取鎖失敗后, 需要判斷當(dāng)前線程是否不要掛起, 正常情況下是都會需要掛起的
                    parkAndCheckInterrupt())
                    interrupted = true; //這里需要設(shè)置一個標(biāo)識位, 是因為park方法無法感知到其他線程調(diào)用了當(dāng)前線程的interrupt方法改變了現(xiàn)場的interrupt狀態(tài)
            }
        } finally {
            if (failed) // 獲取鎖失敗,調(diào)用cancelAcquire方法
                cancelAcquire(node);
        }
    }

acquireQueued 的邏輯,簡要來說,就是如果當(dāng)前節(jié)點(diǎn)的前面是頭節(jié)點(diǎn)(AQS里的頭結(jié)點(diǎn)是哨兵結(jié)點(diǎn)),則試圖獲取鎖,一切順利則成為新的頭節(jié)點(diǎn);否則,會掛起線程。上面的部分分析了獲取鎖的邏輯。下面再來看看釋放鎖的邏輯AbstractQueuedSynchronizer的relase源碼如下:

//釋放鎖需要調(diào)用release方法
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
//釋放成功后,調(diào)用unparkSuccessor方法,讓下個節(jié)點(diǎn)得到鎖
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        
//留給具體的子類來實現(xiàn)
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

//當(dāng)前節(jié)點(diǎn)釋放鎖,下個節(jié)點(diǎn)得到鎖狀態(tài) 
    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
//設(shè)置當(dāng)前節(jié)點(diǎn)的waitStatus
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
//當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)
        Node s = node.next;
//下一個節(jié)點(diǎn)為空情況的處理
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
//下一個節(jié)點(diǎn)不為空,則調(diào)用LockSupport的unpark方法喚醒線程
        if (s != null)
            LockSupport.unpark(s.thread);
    }

ReentrantLock里tryRelease的實現(xiàn)如下所示:

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases; //用state減去release, 這個狀態(tài)是新的狀態(tài) 
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false; //釋放的結(jié)果用free來表示
            if (c == 0) { //為零,表求當(dāng)前沒有錢程點(diǎn)用了,
                free = true;
                setExclusiveOwnerThread(null);
            }
  //設(shè)置狀態(tài) 
            setState(c);
            return free;
        }

Node的狀態(tài)

java.util.concurrent.locks.AbstractQueuedSynchronizer.Node

    /** 表示線程已取消 */
    static final int CANCELLED =  1;
    /** 表示線程等待喚醒 */
    static final int SIGNAL    = -1;
    /** 表示線程等待獲取同步鎖 */
    static final int CONDITION = -2;
    /** 表示共享模式下無條件傳播 */
    static final int PROPAGATE = -3;

CANCELLED(1):表示當(dāng)前結(jié)點(diǎn)已取消調(diào)度。當(dāng)timeout或被中斷(響應(yīng)中斷的情況下),會觸發(fā)變更為此狀態(tài),進(jìn)入該狀態(tài)后的結(jié)點(diǎn)將不會再變化。
SIGNAL(-1):表示后繼結(jié)點(diǎn)在等待當(dāng)前結(jié)點(diǎn)喚醒。后繼結(jié)點(diǎn)入隊時,會將前繼結(jié)點(diǎn)的狀態(tài)更新為SIGNAL。
CONDITION(-2):表示結(jié)點(diǎn)等待在Condition上,當(dāng)其他線程調(diào)用了Condition的signal()方法后,CONDITION狀態(tài)的結(jié)點(diǎn)將從等待隊列轉(zhuǎn)移到同步隊列中,等待獲取同步鎖。
PROPAGATE(-3):共享模式下,前繼結(jié)點(diǎn)不僅會喚醒其后繼結(jié)點(diǎn),同時也可能會喚醒后繼的后繼結(jié)點(diǎn)。
0:新結(jié)點(diǎn)入隊時的默認(rèn)狀態(tài)。
注意,負(fù)值表示結(jié)點(diǎn)處于有效等待狀態(tài),而正值表示結(jié)點(diǎn)已被取消。所以源碼中很多地方用>0、<0來判斷結(jié)點(diǎn)的狀態(tài)是否正常。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容