Java並發之ReentrantLock詳解


    一、入題

        ReentrantLock是Java並發包中互斥鎖,它有公平鎖和非公平鎖兩種實現方式,以lock()為例,其使用方式為:

 

 
ReentrantLock takeLock = new ReentrantLock();  
  
// 獲取鎖  
takeLock.lock();  
  
try {  
    
  // 業務邏輯  
    
} finally {  
  // 釋放鎖  
  takeLock.unlock();  
}  

  

         那么,ReentrantLock內部是如何實現鎖的呢?接下來我們就以JDK1.7中的ReentrantLock的lock()為例詳細研究下。

 

        二、ReentrantLock類的結構

        ReentrantLock類實現了Lock和java.io.Serializable接口,其內部有一個實現鎖功能的關鍵成員變量Sync類型的sync,定義如下:

  

/** Synchronizer providing all implementation mechanics */  
private final Sync sync;  

  

        而這個Sync是繼承了AbstractQueuedSynchronizer的內部抽象類,主要由它負責實現鎖的功能。關於AbstractQueuedSynchronizer我們會在以后詳細介紹,你只要知道它內部存在一個獲取鎖的等待隊列及其互斥鎖狀態下的int狀態位(0當前沒有線程持有該鎖、n存在某線程重入鎖n次)即可,該狀態位也可用於其它諸如共享鎖、信號量等功能。
        Sync在ReentrantLock中有兩種實現類:NonfairSync、FairSync,正好對應了ReentrantLock的非公平鎖、公平鎖兩大類型。

        

        三、獲取鎖主體流程

        ReentrantLock的鎖功能主要是通過繼承了AbstractQueuedSynchronizer的內部類Sync來實現的,其lock()獲取鎖的主要流程如下:

        

        首先,ReentrantLock的lock()方法會調用其內部成員變量sync的lock()方法;

        其次,sync的非公平鎖NonfairSync或公平鎖FairSync實現了父類AbstractQueuedSynchronizer的lock()方法,其會調用acquire()方法;

        然后,acquire()方法則在sync父類AbstractQueuedSynchronizer中實現,它只有一段代碼:

 
public final void acquire(int arg) {  
    if (!tryAcquire(arg) &&  
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  
        selfInterrupt();  
}  

  

      通過tryAcquire()方法試圖獲取鎖,獲取到直接返回結果,否則通過嵌套調用acquireQueued()、addWaiter()方法將請求獲取鎖的線程加入等待隊列,如果成功的話,將當前請求線程阻塞,and,over!

         隊列如何實現及如何添加到隊列中以后再做詳細分析!這里只關注ReentrantLock的實現邏輯。

        上述就是公平鎖、非公平鎖實現獲取鎖的主要流程,而針對每種鎖來說,其實現方式有很大差別,主要就體現在各自實現類的lock()和tryAcquire()方法中。在sync的抽象類Sync及其抽象父類AbstractQueuedSynchronizer中,lock()方法和tryAcquire()方法被定義為抽象方法或者未實現,而是由具體子類去實現:

 

 
/** 
 * Performs {@link Lock#lock}. The main reason for subclassing 
 * is to allow fast path for nonfair version. 
 */  
abstract void lock();  

  

  

/** 
     * Attempts to acquire in exclusive mode. This method should query 
     * if the state of the object permits it to be acquired in the 
     * exclusive mode, and if so to acquire it. 
     * 
     * <p>This method is always invoked by the thread performing 
     * acquire.  If this method reports failure, the acquire method 
     * may queue the thread, if it is not already queued, until it is 
     * signalled by a release from some other thread. This can be used 
     * to implement method {@link Lock#tryLock()}. 
     * 
     * <p>The default 
     * implementation throws {@link UnsupportedOperationException}. 
     * 
     * @param arg the acquire argument. This value is always the one 
     *        passed to an acquire method, or is the value saved on entry 
     *        to a condition wait.  The value is otherwise uninterpreted 
     *        and can represent anything you like. 
     * @return {@code true} if successful. Upon success, this object has 
     *         been acquired. 
     * @throws IllegalMonitorStateException if acquiring would place this 
     *         synchronizer in an illegal state. This exception must be 
     *         thrown in a consistent fashion for synchronization to work 
     *         correctly. 
     * @throws UnsupportedOperationException if exclusive mode is not supported 
     */  
    protected boolean tryAcquire(int arg) {  
        throw new UnsupportedOperationException();  
    }  

  

        下面,我們分別研究下非公平鎖和公平鎖的實現。

 

        四、非公平鎖NonfairSync

        1、lock()方法

 
/** 
 * Performs lock.  Try immediate barge, backing up to normal 
 * acquire on failure. 
 */  
final void lock() {  
    if (compareAndSetState(0, 1))  
        setExclusiveOwnerThread(Thread.currentThread());  
    else  
        acquire(1);  
}  

  

        通過代碼可以看到,非公平鎖上來就無視等待隊列的存在而搶占鎖,通過基於CAS操作的compareAndSetState(0, 1)方法,試圖修改當前鎖的狀態,這個0表示AbstractQueuedSynchronizer內部的一種狀態,針對互斥鎖則是尚未有線程持有該鎖,而>=1則表示存在線程持有該鎖,並重入對應次數,這個上來就CAS的操作也是非公共鎖的一種體現,CAS操作成功的話,則將當前線程設置為該鎖的唯一擁有者。

        搶占不成功的話,則調用父類的acquire()方法,按照上面講的,繼而會調用tryAcquire()方法,這個方法也是由最終實現類NonfairSync實現的,如下:

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

        2、tryAcquire()

        而這個nonfairTryAcquire()方法實現如下:

/** 
 * Performs non-fair tryLock.  tryAcquire is 
 * implemented in subclasses, but both need nonfair 
 * try for trylock method. 
 */  
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;  
}  

        還是上來先判斷鎖的狀態,通過CAS來搶占,搶占成功,直接返回true,如果鎖的持有者線程為當前線程的話,則通過累加狀態標識重入次數。搶占不成功,或者鎖的本身持有者不是當前線程,則返回false,繼而后續通過進入等待隊列的方式排隊獲取鎖。可以通過以下簡單的圖來理解:

        

 

        五、公平鎖FairSync

        1、lock()

        公平鎖的lock()方法就比較簡單了,直接調用acquire()方法,如下:

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

  

        2、tryAcquire()

        公平鎖的tryAcquire()方法也相對較簡單,如下:

/** 
 * Fair version of tryAcquire.  Don't grant access unless 
 * recursive call or no waiters or is first. 
 */  
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;  
}  

  

        當前線程會在得到當前鎖狀態為0,即沒有線程持有該鎖,並且通過!hasQueuedPredecessors()判斷當前等待隊列沒有前繼線程(也就是說,沒有比我優先級更高的線程在請求鎖了)獲取鎖的情況下,通過CAS搶占鎖,並設置自己為鎖的當前擁有者,當然,如果是重入的話,和非公平鎖處理一樣,通過累加狀態位標記重入次數。

        而一旦等待隊列中有等待者,或當前線程搶占鎖失敗,則它會乖乖的進入等待隊列排隊等待。公平鎖的實現大致如下:

         

 

        六、默認實現

        ReentrantLock的默認實現為非公平鎖,如下:

/** 
 * Creates an instance of {@code ReentrantLock}. 
 * This is equivalent to using {@code ReentrantLock(false)}. 
 */  
public ReentrantLock() {  
    sync = new NonfairSync();  
}  

  

        當然,你也可以通過另外一個構造方法指定鎖的實現方式,如下:

 
/** 
 * Creates an instance of {@code ReentrantLock} with the 
 * given fairness policy. 
 * 
 * @param fair {@code true} if this lock should use a fair ordering policy 
 */  
public ReentrantLock(boolean fair) {  
    sync = fair ? new FairSync() : new NonfairSync();  
}  

  

 

        七、其它
        即便是公平鎖,如果通過不帶超時時間限制的tryLock()的方式獲取鎖的話,它也是不公平的,因為其內部調用的是sync.nonfairTryAcquire()方法,無論搶到與否,都會同步返回。如下:

 
public boolean tryLock() {  
    return sync.nonfairTryAcquire(1);  
}  

  

        但是帶有超時時間限制的tryLock(long timeout, TimeUnit unit)方法則不一樣,還是會遵循公平或非公平的原則的,如下:

 

public boolean tryLock(long timeout, TimeUnit unit)  
        throws InterruptedException {  
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));  
}  

  

        其它流程都比較簡單,讀者可自行翻閱Java源碼查看!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM