可重入鎖和不可重入鎖,遞歸鎖和非遞歸鎖


首先引入概念:

可重入鎖:廣義上的可重入鎖指的是可重復可遞歸調用的鎖,在外層使用鎖之后,在內層仍然可以使用,並且不發生死鎖(前提得是同一個對象或者class),這樣的鎖就叫做可重入鎖,

java里面最常見的鎖,ReentrantLock和synchronized都是可重入鎖

不可重入鎖:不可重入鎖,與可重入鎖相反,不可遞歸調用,遞歸調用就發生死鎖。即若當前線程執行某個方法已經獲取了該鎖,那么在方法中嘗試再次獲取鎖時,就會獲取不到被阻塞。

 

如下圖設計一個不可重入鎖。

public class Lock{
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException{
        while(isLocked){    
            wait();
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();
    }
}

 

public class Test{
    Lock lock = new Lock();

/**
調用打印的方法
*/
public void print(){ lock.lock(); doAdd(); lock.unlock(); } public void doAdd(){ lock.lock(); //sout("執行業務代碼") lock.unlock(); } }

場景說明:假設某業務下需要調用Test類里面的print()方法,假設他的線程命名為T0,這時T0會執行lock.lock()方法,首先對於這個對象來說,isLocked屬性的初始值時false,因此它進入while循環的時候

判斷為false,直接跳出當前循環,把對象的isLocked屬性變為true,相當於拿到了鎖,這時T0再去執行doAdd(),由於要保證原子性,因此在doAdd方法里面也加入了lock鎖,這時,線程還是T0線程,但

由於isLocked屬性由於第一次加鎖已經變成true,因此,T0線程執行到了wait()方法就處於等待,導致doAdd里面的業務代碼無法執行,導致線程阻塞。

 

下面我們來創建一個可重入鎖

public class Lock{
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock()
            throws InterruptedException{
        Thread thread = Thread.currentThread();
        while(isLocked && lockedBy != thread){
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = thread;
    }
    public synchronized void unlock(){
        if(Thread.currentThread() == this.lockedBy){
            lockedCount--;
            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
}
public class Test{
    Lock lock = new Lock();

    /**
     調用打印的方法
    */
    public void print(){
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd(){
        lock.lock();
        //sout("執行業務代碼")
        lock.unlock();
    }
}

       場景如上描述,假設線程T0進來了,調用print方法,lock.lock(),第一步首先拿到當前線程,由於初始的islocked為false,同時lockedby為null 和當前線程T0不相等,false &&true 得到還是false ,因此直接跳出while循環,線程不等待,將isLocked設置為true,同時設置當前鎖的數量從0加上1變成1,並且設置lockby為當前線程T0,此時T0繼續執行doAdd方法,當執行doAdd()里面的lock.lock()時,同樣還是線程T0,因此while循環的判斷變成了true&& false,最終拿到的還是false,這時線程還是不等待,isLocked還是true,同時當前線程擁有的鎖變成了2,lockedby還是T0,這時假設又有T1,T2線程進來,當他們執行print()方法,執行到了lock.lock(),首先拿到當前線程是T1,而lockedby是T0,while循環的條件判斷是true&&true,則T1就處於了等待狀態,只有當T0執行完doAll()的業務代碼,並第一次釋放鎖,lock.unlock(),當前線程的計數器減去1,這時T0再去執行print方法里面的lock.unlock(),這時線程T0,計數器變量變成了0,同時設置isLocked為false,執行notify方法,喚醒其他的線程,后續線程搶奪資源拿到鎖之后,即可實現同步安全的執行。

      總結如下:

      可重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之后 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。

      不可重入鎖,也可以叫非遞歸鎖,就是拿不到鎖的情況會不停自旋循環檢測來等待,不進入內核態沉睡,而是在用戶態自旋嘗試。

       同一個線程可以多次獲取同一個遞歸鎖,不會產生死鎖。而如果一個線程多次獲取同一個非遞歸鎖,則會產生死鎖。


免責聲明!

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



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