AQS相關(lock、unlock、await、signal)

minororange發表於2021-11-18

節點狀態

/** 節點已被取消 */
static final int CANCELLED =  1;
/** 
 *  後繼節點的執行緒處於等待狀態,
 *  而當前節點的執行緒如果釋放了同步狀態或者被取消,
 *  那麼就會通知後繼節點,讓後繼節點的執行緒能夠執行 
 */
static final int SIGNAL    = -1;
/** 
 * 節點在等待佇列中,節點執行緒等待在Condition上,
 * 不過當其他的執行緒對Condition呼叫了signal()方法後,
 * 該節點就會從等待佇列轉移到同步佇列中,然後開始嘗試對同步狀態的獲取 
 */
static final int CONDITION = -2;
/**
 * 表示下一次的共享式同步狀態獲取將會無條件的被傳播下去
 */
static final int PROPAGATE = -3;

參考資料

lock 方法流程

AQS相關(lock、unlock、await、signal)

原始碼:

  • 公平鎖
       final void lock() {
            // 呼叫父類的 acquire
            acquire(1);
        }
        // 父類
        public final void acquire(int arg) {
          // 嘗試搶鎖
          if (!tryAcquire(arg) &&
          // 搶鎖失敗加入佇列排隊獲取鎖,此步驟為自旋阻塞,addWaiter對應流程圖2
          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
          // 如果在佇列中搶鎖失敗,則關閉當前執行緒
              selfInterrupt();
        }
        // 嘗試搶鎖
        protected final boolean tryAcquire(int acquires) {
            // 獲取當前執行緒
            final Thread current = Thread.currentThread();
            // 獲取當前 state 值
            int c = getState();
            // 如果當前 state 為沒人佔用狀態
            if (c == 0) {
                // 同步佇列中是否有搶鎖的執行緒,對應流程圖1
                if (!hasQueuedPredecessors() &&
                    // 如果前面沒有搶鎖的執行緒,CAS 改寫 state
                    compareAndSetState(0, acquires)) {
                    // 如果寫成功則將當前鎖的持有人標記為當前執行緒
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 當前 state 為當前執行緒佔用
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • 非公平鎖
        final void lock() {
            // CAS 改寫 state,寫成功代表搶鎖成功
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 上面寫失敗了就呼叫父級的 acquire 方法,同公平鎖
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
          return nonfairTryAcquire(acquires);
        }
       final boolean nonfairTryAcquire(int acquires) {
            // 獲取當前執行緒
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 比公平鎖少一步判斷同步佇列的步驟
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • acquireQueued 佇列阻塞搶鎖
   // 對應流程3
   final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 獲取當前結點的上一個節點
                final Node p = node.predecessor();
                // 如果上一個節點為 head,則嘗試獲取鎖
                if (p == head && tryAcquire(arg)) {
                    // 搶鎖成功後將 head 設為當前節點
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 搶鎖失敗,判斷當前執行緒是否應該關閉
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            // 搶鎖失敗,進行取消操作
            if (failed)
                cancelAcquire(node);
        }
    }

wait & signal (等待與喚醒)

阻塞佇列中,如果當前佇列為空,則會呼叫 notEmpty.await() 方法,notEmptyConditionObject,通常阻塞佇列中會有兩個 ConditionObject,一個負責【取(notEmpty)】的執行緒,一個負責【存(notFull)】的執行緒。一個 ConditionObject 就是一個等待佇列,呼叫 await() 方法就是將當前執行緒從同步佇列移到等待佇列中。

AQS相關(lock、unlock、await、signal)

原始碼解析:

        public final void await() throws InterruptedException {
            // 如果當前執行緒是中斷狀態則丟擲中斷異常
            if (Thread.interrupted())
                throw new InterruptedException();
            // 將節點加入等待佇列,對應流程1
            Node node = addConditionWaiter();
            // 釋放當前節點所持的鎖,對應流程2
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            // 如果當前節點不在同步佇列中,則掛起等待喚醒,對應流程3
            // 當執行緒被喚醒時,節點會加入同步佇列,則會跳出 while,進入下面的邏輯
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            // 進入同步佇列搶鎖
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
  • signal
        public final void signal() {
            // 如果不是當前持鎖的執行緒操作,則丟擲異常
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                // 如果等待佇列有執行緒在等待
                doSignal(first);
        }
         private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
    final boolean transferForSignal(Node node) {
        /*
         * 如果不能修改狀態,則表示當前節點已被取消
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        // 將節點加入同步佇列,返回當前節點的上一個節點
        Node p = enq(node);
        // 獲取上一個節點的等待狀態
        int ws = p.waitStatus;
        // 如果上一個節點為取消
        // 或者上一個節點的狀態已經時 SIGNAL 狀態
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 喚醒當前節點
            LockSupport.unpark(node.thread);
        // 否則就交給同步佇列進行喚醒,每個執行緒釋放鎖時都會去喚醒他的下一個節點
        return true;
    }
  • realse
    public final boolean release(int arg) {
        // 嘗試釋放鎖
        if (tryRelease(arg)) {
            Node h = head;
            // 釋放成功後喚醒下一個節點(如果有的話),對應流程4
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章