java併發- ReentrantLock

ReentrantLock

一、源碼

類圖

  1. ReentrantLock實現了Lock接口,加鎖和解鎖都需要顯式寫出,注意一定要在適當時候unlock。
    ReentrantLock的類圖

公平鎖和非公平鎖

  • 公平鎖:線程獲取鎖的順序和調用lock的順序一樣,FIFO;
  • 非公平鎖:線程獲取鎖的順序和調用lock的順序無關,全憑運氣。

ps:
ReentrantLock默認使用非公平鎖是基於性能考慮,公平鎖爲了保證線程規規矩矩地排隊,需要增加阻塞和喚醒的時間開銷。如果直接插隊獲取非公平鎖,跳過了對隊列的處理,速度會更快。

public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock的內部類Sync繼承了AQS,分爲公平鎖FairSync和非公平鎖NonfairSync。
ReentrantLock的公平鎖和非公平鎖都是基於AbstractQueuedSynchronizer(AQS)實現的。ReentrantLock使用的是AQS的排他鎖模式,由於AQS除了排他鎖模式還有共享鎖模式,本文僅對ReentrantLock涉及到的排他鎖模式部分的內容進行介紹,關於共享鎖模式的部分會在 CountDownLatch 源碼淺析一文中介紹。

公平鎖和非公平鎖獲取鎖的對比

公平鎖加鎖調用順序

  1. ReentrantLock:lock()
  2. FairSync:lock()
  3. AbstractQueuedSynchronizer:acquire(int acquires)
  4. FairSync:tryAcquire(int acquires)
    其中第四步纔開始加鎖,源碼如下:
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. ReentrantLock:lock()
  2. NonfairSync:lock()
  3. Sync:nonfairTryAcquire(int acquires)
  4. AbstractQueuedSynchronizer:compareAndSetState(int expect, int update)

第三步源碼

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;
        }

對比

  1. 可以看出公平鎖的加鎖多了一個判斷!hasQueuedPredecessors(), 即加入了同步隊列中當前節點是否有前驅節點的判斷,如果該方法返回true,則表示有線程比當前線程更早地請求獲取鎖,因此需要等待前驅線程獲取並釋放鎖之後才能繼續獲取鎖。
  2. 加鎖最終都是調用的AQS的compareAndSetState方法
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 設置鎖的持有者爲當前線程
protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
 }

公平鎖和非公平鎖的解鎖

解鎖步驟是一樣的

調用順序

  1. ReentrantLock:unlock()
  2. AbstractQueuedSynchronizer:release(int acquires)
  3. Sync:tryRelease(int acquires)

其中acquires=1,看下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;
        }

分析

公平鎖在釋放鎖的最後寫volatile變量state;在獲取鎖時首先讀這個volatile變量。根據volatile的happens-before規則,釋放鎖的線程在寫volatile變量之前可見的共享變量,在獲取鎖的線程讀取同一個volatile變量後將立即變的對獲取鎖的線程可見

二、原理分析

CAS[原子性]+volatile[可見性]

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