1. 程式人生 > >ReentrantLock之非公平鎖源碼分析

ReentrantLock之非公平鎖源碼分析

read gets 允許 span https clas color protected 版本

  本文分析的ReentrantLock所對應的Java版本為JDK8。

  在閱讀本文前,讀者應該知道什麽是CAS、自旋。

  由於ReentrantLock的公平鎖和非公平鎖中有許多共同代碼,本文只會對這兩種鎖的不同之處加以分析,所以如果讀者對公平鎖不熟的話,強烈建議先看我的上篇博客——ReentrantLock之公平鎖源碼分析。

本文大綱

  1.ReentrantLock非公平鎖簡介
  2.lock方法
  3.unlock方法
  4.公平鎖與非公平鎖的異同

1. ReentrantLock非公平鎖簡介

  ReentrantLock是JUC(java.util.concurrent)包中Lock接口的一個實現類,它是基於AbstractQueuedSynchronizer(下文簡稱AQS)來實現鎖的功能。ReentrantLock的內部類Sync繼承了AbstractQueuedSynchronizer,Sync又有FairSync和NonFairSync兩個子類。FairSync實現了公平鎖相關的操作,NonFairSync實現了非公平鎖相關的操作。它們之間的關系如下:

技術分享圖片

  非公平鎖的不公平之處主要體現在,對於一個新來的線程,它會直接去搶占鎖,不理會鎖是否已經被占用或者該鎖的等待隊列中已經有其它的等待線程,如果搶占失敗再進入等待隊列隊尾。

  下面這段代碼展示了非公平鎖的使用方法:

private final Lock lock = new ReentrantLock(); // 調用ReentrantLock的空參構造方法,默認創建非公平鎖

public void method() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
} finally { lock.unlock(); } }

2. lock方法

  ReentrantLock的lock方法調用了NonFairSync的lock方法:

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

  NonFairSync的lock方法:

final void lock() {
    if (compareAndSetState(0, 1)) // 註意!!!在非公平鎖中,會先用CAS的方式去嘗試更改鎖的狀態,即嘗試去獲取鎖,不管鎖是否被其他線程持有,也不理會等待隊列中是否有等待的線程
setExclusiveOwnerThread(Thread.currentThread()); // 獲取鎖成功,則將當前占有鎖的線程設置為當前線程 else acquire(1); // CAS獲取鎖失敗,則進入acquire方法 }

  acquire方法:

public final void acquire(int arg) {
    if (!tryAcquire(arg) && // 這裏將調用NonfairSync的tryAcquire方法
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果獲取鎖失敗,執行acquireQueued方法,將把當前線程排入隊尾
        selfInterrupt();
}

  tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

  nonfairTryAcquire方法:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread(); 
    int c = getState(); // 獲取鎖的狀態
    if (c == 0) { // 如果狀態是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;
}

  請註意上文的代碼註釋中的有兩個“註意!!!”,這兩處就是公平鎖和非公平鎖的區別所在。

  lock方法中,其它剩余的代碼就和公平鎖中的一樣了,如果讀者還需了解lock方法中後面的代碼,請參見我的上篇博客。

3. unlock方法

  和公平鎖的unlock方法一樣,請允許我偷個懶~ 還是參見我的上篇博客吧。

4. 公平鎖與非公平鎖的異同

  • 在公平鎖(FairSync)的lock方法中,會直接調用aquire方法;但是在非公平鎖(NonfairSync)的lock方法中,會先采用CAS的方式去獲取鎖,不管是否有其他線程已經占有鎖或者是否有其他線程在等待隊列中。
  • 公平鎖(FairSync)調用的tryAcquire方法中,會先去檢查等待隊列中是否有等待的線程;但是在非公平鎖(NonfairSync)調用的nonfairTryAcquire不會去檢查等待隊列。
  • 無論公平鎖還是非公平鎖,對於排隊中的線程,都能保證排在前面的線程一定比排在後面的線程優先獲得鎖。

ReentrantLock之非公平鎖源碼分析