12、ReentrantLock中的公平鎖和非公平鎖的原理

ReentrantLock

ReentrantLock內部是通過AQS實現鎖的功能,有公平鎖和非公平鎖兩種實現。

  1. 公平鎖,即鎖的獲取順序按線程申請鎖的先後順序。
  2. 非公平鎖,當一個線程t1申請鎖時,鎖剛好釋放。即使已有其他線程在t1之前申請鎖排隊,線程t1還是會獲取鎖。這樣減少了線程的等待喚醒的可能,減少上下文切換帶來的開銷。因爲獲取鎖的順序和申請順序可能不一致所以叫非公平鎖。

前置技能(先了解前置技能纔好看懂)

  1. AQS
  2. CLH

ReentrantLock中的Sync

Sync是個抽象類,非公平鎖和公平鎖都基於這個類實現。這裏實現了非公平的佔用鎖方法。

 abstract static class Sync extends AbstractQueuedSynchronizer {
        ...
        /**
         *非公平鎖的佔用方法
         */
        final boolean nonfairTryAcquire(int acquires) {
            //獲取當前的線程
            final Thread current = Thread.currentThread();
            //獲取當前的鎖佔用狀態
            int c = getState();
            //state==0則說明沒有線程佔用鎖
            if (c == 0) {
                //此時會直接把鎖給當前線程,而不去判斷CLH隊列中的是否已有等待線程。
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //這裏是重入鎖的處理,當前線程重入鎖,state+1
            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;
        }

        ...
    }

非公平鎖

ReentrantLock中的非公平鎖實現

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

       
        final void lock() {
            //當前線程進來先直接用cas嘗試佔用鎖,失敗再調用acquire
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        
        //acquire會調用tryAcquire判斷佔用鎖是否成功,這裏直接調用了Sync的非公平鎖處理方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
}

公平鎖

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         *公平鎖的處理
         * 
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //會判斷隊列中是否有等待線程,有則獲取鎖失敗,進入隊列等待。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }


總結

  1. 非公平鎖減少了線程發生等待喚醒的可能,節省了上下文切換的開銷。
  2. 公平鎖適合鎖持有事件較長或者線程申請鎖的間隔事件相對長的情況。
  3. 總的來所,公平鎖的開銷比非公平鎖大,所以ReentrantLock默認支持的是非公平鎖。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章