java原始碼-ReentrantReadWriteLock寫鎖介紹

晴天哥發表於2018-09-01

開篇

 這篇文章主要從原始碼角度講解ReentrantReadWriteLock的WriteLock的加鎖和減鎖過程。
 ReentrantReadWriteLock的WriteLock加鎖解鎖過程依賴於AbstractQueuedSynchronizer(AQS)類,所以有些相同的邏輯可以看看ReentrantLock的邏輯。

加鎖過程

  • WriteLock的lock()內部通過sync.acquire(1)獲取鎖。
  • 嘗試通過tryAcquire獲取寫鎖,如果獲取成功那麼就成功佔用寫鎖。
  • 獲取寫鎖失敗後,將當前執行緒新增到寫鎖喚醒佇列當中acquireQueued(addWaiter(Node.EXCLUSIVE), arg))。
  • acquireQueued(addWaiter(Node.EXCLUSIVE), arg))的操作邏輯和ReentrantLock的邏輯一致。
    public static class WriteLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -4992448646407690164L;
        private final Sync sync;

        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        public void lock() {
            sync.acquire(1);
        }
    }

    public final void acquire(int arg) {
        // 1、先嚐試獲取鎖tryAcquire
        // 2、獲鎖失敗就addWaiter操作
        // 3、acquireQueued判斷是否喚醒
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire過程

  • 獲取鎖狀態state變數,並獲取寫鎖佔用的計數值。
  • 當前state不為0,如果寫鎖狀態為0說明讀鎖被佔用,返回鎖佔用失敗。
  • 鎖狀態state不為空且佔鎖執行緒為當前執行緒,說明鎖被其他執行緒佔用返回鎖佔用失敗。
  • 寫鎖重入數溢位,返回鎖佔用失敗。
  • 如果寫鎖阻塞 或者 設定state狀態失敗,返回鎖佔用失敗。
  • 設定當前鎖佔用執行緒為當前執行緒,返回鎖佔用成功。
    protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            // 獲取鎖狀態state變數
            int c = getState();
            // 獲取寫鎖佔用的計數
            int w = exclusiveCount(c);
            // 如果鎖狀態state不為0
            if (c != 0) {
                // 1、當前state不為0,如果寫鎖狀態為0說明讀鎖此時被佔用,說明鎖被讀鎖佔用
                // 2、鎖狀態state不為空且佔鎖執行緒為當前執行緒(屬於鎖重入),說明鎖被其他執行緒佔用
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                // 寫鎖重入數溢位
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // 寫鎖獲取成功返回成功標記
                setState(c + acquires);
                return true;
            }
            // 如果寫鎖阻塞 或者 設定state狀態失敗,那麼就代表獲鎖失敗
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            // 設定當前鎖佔用執行緒為當前執行緒
            setExclusiveOwnerThread(current);
            return true;
        }

解鎖過程

  • WriteLock的unlock()內部通過sync. release(1)釋放鎖。
  • 嘗試通過tryRelease()方法來釋放鎖並喚醒下一個等待執行緒。
  • 在喚醒過程中需要仔細看看讀寫鎖等待執行緒喚醒的細節,待補充
    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;
    }

tryRelease過程

  • 判斷當前執行緒和鎖佔用執行緒不一致isHeldExclusively()丟擲異常。
  • 鎖狀態減去當前釋放動作傳入引數nextc = getState() – releases。
  • 判斷鎖狀態的寫狀態為0就表明當前執行緒已經完全釋放鎖。
  • 當前執行緒完全釋放鎖,然後設定鎖佔用執行緒為null並設定鎖狀態。
    protected final boolean tryRelease(int releases) {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        int nextc = getState() - releases;
        boolean free = exclusiveCount(nextc) == 0;
        if (free)
            setExclusiveOwnerThread(null);
        setState(nextc);
        return free;
    }


相關文章