Java併發--重入鎖ReentrantLock如何實現重入性

前言

重入鎖ReentrantLock,就是支持線程重進入的鎖,它表示該鎖能夠支持一個線程對資源的重複加鎖。synchronized關鍵字隱士的支持鎖的可重入性。利用synchronized,執行線程在獲取鎖之後仍然可以多次獲取鎖。而ReentrantLock是通過顯示去完成鎖的可重入性,接下來我們探討ReentrantLock如何實現可重入性。

正文

重入性是指任意現場在獲取到鎖之後再次獲取該鎖而不會被鎖阻塞,重入性的實現主要需要解決兩個問題。

  • 線程再次獲取鎖。鎖需要去識別獲取鎖的線程是否爲當前佔據鎖的線程,如果是,則獲取鎖成功。
  • 鎖的最終釋放。線程重複n次獲取了鎖,當第n次釋放鎖後,其他線程能夠獲取到鎖。

線程獲取鎖

以非公平性(默認)實現爲例,當線程通過ReentrantLock的lock()方法再次獲取鎖時,會調用nonfairTryAcquire方法獲取同步狀態,在該方法中判斷獲取鎖的線程是否爲當前持有鎖的線程。

        final boolean nonfairTryAcquire(int acquires) {
            // 獲取當前線程
            final Thread current = Thread.currentThread();
            // 獲取同步狀態
            int c = getState();
            // 同步狀態爲0,表示沒有線程獲取鎖
            if (c == 0) {
                // CAS獲取同步狀態
                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;
        }

同步狀態爲0不難理解,說明目前沒有任何線程獲取到鎖。而當同步狀態不爲0時,通過判斷當前線程是否爲獲取鎖的線程來決定操作是否成功,如果是獲取鎖的線程再次請求,則將同步狀態的值進行增加並返回true,表示同步狀態成功。

注:setExclusiveOwnerThread(current)方式是設置AbstractOwnableSynchronizer的成員變量exclusiveOwnerThread,該變量表明獨佔式鎖的線程所有者。

鎖釋放

線程通過ReentrantLock的unlock()方法來釋放鎖,會調用tryRelease方法釋放同步狀態。

        protected final boolean tryRelease(int releases) {
            // 當前同步狀態減去釋放的同步數據量
            int c = getState() - releases;
            // 判斷當前線程是否爲獲取鎖的線程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            // 標記鎖釋放後是否繼續被線程佔用
            boolean free = false;
            if (c == 0) {
                free = true;
                // 設置沒有線程佔用鎖
                setExclusiveOwnerThread(null);
            }
            // 更新同步狀態
            setState(c);
            return free;
        }

從tryRelease方法中可以看到,同步狀態是否爲0是鎖是否最終被釋放的依據,當且僅當同步狀態爲0,將佔有線程設置爲null,並返回true,表示釋放成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章