1. 程式人生 > 實用技巧 >可重入鎖基本原理

可重入鎖基本原理

轉自:http://www.jianshu.com/p/007bd7029faf

簡單鎖

在講述簡單鎖的實現之前,我們先來看一個鎖的應用例子:

public class Counter{
    private Lock lock = new Lock();
    private int count = 0;
    public int inc(){
        lock.lock();
        this.count++;
        lock.unlock();
        return count;
    }
}

上面的程式中,由於this.count++這一操作分多步執行,在多執行緒環境中可能出現結果不符合預期的情況,這段程式碼稱之為臨界區,所以需要使用lock來保證其原子性。

Lock的實現:

public class Lock{
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException{
        while(isLocked){    //不用if,而用while,是為了防止假喚醒
            wait();
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();
    }
}

說明:當isLocked為true時,呼叫lock()的執行緒在wait()阻塞。為防止該執行緒虛假喚醒,程式會重新去檢查isLocked條件。如果isLocked為false,當前執行緒會退出while(isLocked)迴圈,並將isLocked設回true,讓其它正在呼叫lock()方法的執行緒能夠在Lock例項上加鎖。當執行緒完成了臨界區中的程式碼,就會呼叫unlock()。執行unlock()會重新將isLocked設定為false,並且喚醒其中一個處於等待狀態的執行緒。

鎖的可重入性

同樣,先舉例來說明鎖的可重入性:

public class UnReentrant{
    Lock lock = new Lock();
    public void outer(){
        lock.lock();
        inner();
        lock.unlock();
    }
    public void inner(){
        lock.lock();
        //do something
        lock.unlock();
    }
}

outer中呼叫了inner,outer先鎖住了lock,這樣inner就不能再獲取lock。其實呼叫outer的執行緒已經獲取了lock鎖,但是不能在inner中重複利用已經獲取的鎖資源,這種鎖即稱之為不可重入。通常也稱為自旋鎖。相對來說,可重入就意味著:執行緒可以進入任何一個它已經擁有的鎖所同步著的程式碼塊。

可重入鎖的基本原理

我們在上面的Lock基礎上作一些修改:

public class Lock{
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock()
        throws InterruptedException{
        Thread callingThread = Thread.currentThread();
        while(isLocked && lockedBy != callingThread){
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
  }
    public synchronized void unlock(){
        if(Thread.curentThread() == this.lockedBy){
            lockedCount--;
            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
}
  • lockBy:儲存已經獲得鎖例項的執行緒,在lock()判斷呼叫lock的執行緒是否已經獲得當前鎖例項,如果已經獲得鎖,則直接跳過while,無需等待。
  • lockCount:記錄同一個執行緒重複對一個鎖物件加鎖的次數。否則,一次unlock就會解除所有鎖,即使這個鎖例項已經加鎖多次了。