1. 程式人生 > >ReentrantLock的加鎖和解鎖原理

ReentrantLock的加鎖和解鎖原理

ReentrantLock的實現依賴於Java同步器框架AQS。
AQS使用一個整型的votalie變數(命名為state)來維護同步狀態。

  • ReentrantLock分為公平鎖和非公平鎖

公平鎖

使用公平鎖時,加鎖方法lock()的呼叫軌跡:

  1. ReentrantLock:lock()
  2. FairSync:lock()
  3. AbstractQueuedSynchronizer:acquire(int arg)
  4. ReentrantLock:tryAcquire(int acquires)

第4步開始加鎖,原始碼如下

protected final boolean tryAcquire(int acquires) {
            //獲取當前執行緒
            final Thread current = Thread.currentThread();
            //通過AQS獲取同步狀態
            int c = getState();
            //同步狀態為0,說明臨界區處於無鎖狀態,
            if (c == 0) {
                //判斷該執行緒是否在隊首,然後修改同步狀態,即加鎖
                if (isFirst(current)&&compareAndSetState(0, acquires)) {
                    //將當前執行緒設定為鎖的owner
                    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;
        }

其解鎖方法unlock()呼叫軌跡如下:

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

第3步才開始真正釋放鎖

protected final boolean tryRelease(int releases){
    int c = getState() - releases;
    if(Tread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException()
    boolean free = false;
    if(c == 0){//如果state減去releases為0,則釋放鎖
        free = true;
        setExcluiveOwnerThread(null);//將獲取鎖的執行緒設為null,即無鎖
    }
    setState(c);//設定state,若c為0,則釋放鎖
    return free;
}

非公平鎖

非公平鎖的釋放與公平鎖一樣,這裡僅探討鎖的獲取。

使用非公平鎖時,加鎖方法lock()呼叫軌跡如下:

  1. ReentrantLock:lock()
  2. NonfairSync:lock()
  3. AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        
        if (compareAndSetState(0, 1))//對state進行CAS
            //若成功則加鎖   
            setExclusiveOwnerThread(Thread.currentThread();
        else
            acquire(1);
    }
}

第3步進行加鎖。
該方法使用原子操作的方式更新state變數。

總結:

  • 公平鎖和非公平鎖的釋放時,最後都要寫一個volatile變數state。
  • 公平鎖獲取時,首先會讀volatile變數。
  • 非公平鎖的獲取,首先會用CAS更新volatile變數,這個操作同時具有volatlie寫和volatile讀的記憶體語義。