第五章 Synchronized的性質(zhì)

一、可重入
1,指的是同一個(gè)線程的外層函數(shù)(synchornized)獲得鎖之后,內(nèi)層函數(shù)(synchornized)可以再次獲取該鎖。
2,線程t1拿到了Monitor,想再次使用Monitor進(jìn)入下一個(gè)同步方法,如果要釋放掉Monitor,然后重新和其他線程競(jìng)爭(zhēng)Monitor就是不可重入;如果無需提前釋放Monitor,直接拿著Monitor去用,就是可重入。
3,可重入鎖也稱為遞歸鎖。
4,在java中,有兩個(gè)可重入鎖,一個(gè)就是synchornized關(guān)鍵字,還有一個(gè)就是ReentrantLock.
5,可重入鎖的好處: 避免死鎖,提升封裝性,避免了一次次的解鎖加鎖。
為什么能避免死鎖呢?
假設(shè)synchornized不具備可重入性,t1進(jìn)入一個(gè)synchornized方法1獲取到了Monitor,然后又要進(jìn)入下一次synchornized方法2,這個(gè)時(shí)候synchornized方法1沒有執(zhí)行完所以是不會(huì)釋放鎖的,這個(gè)時(shí)候因?yàn)椴痪邆淇芍厝胄?,所以進(jìn)入synchornized方法2又要求重新獲取Monitor,這樣就造成了死鎖。
所以synchornized的可重入性可以避免線程永遠(yuǎn)等待下去的現(xiàn)象。

6,可重入的scope:
java的synchornized關(guān)鍵字默認(rèn)加鎖的范圍是線程,但是linux中的pthread,它互斥體的默認(rèn)加鎖行為是以調(diào)用為粒度的。

線程而非調(diào)用(用三種情況來說明和pthread的區(qū)別)
情況1:證明同一個(gè)方法是可重入的

package synchornized;

/**
 * Synchronized 可重入粒度測(cè)試:
 * 證明同一個(gè)方法是可重入的
 */
public class SynchronizedRecursion10 implements Runnable{
    int num = 0;
    public static void main(String[] args) throws InterruptedException {
        SynchronizedRecursion10 runnable = new SynchronizedRecursion10();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    
    private synchronized void syn(){
        System.out.println("get monitor");
        num++;
        if (num==3){
            return;
        }else {
            syn();
        }
    }
    
    @Override
    public void run() {
        syn();
    }
}

情況2:證明可重入不要求是同一個(gè)方法

package synchornized;

/**
 * Synchronized 可重入粒度測(cè)試:
 * 證明可重入不要求是同一個(gè)方法
 */
public class SynchronizedRecursion11 implements Runnable{
    int num = 0;
    public static void main(String[] args) throws InterruptedException {
        SynchronizedRecursion11 runnable = new SynchronizedRecursion11();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    
    private synchronized void syn1(){
        System.out.println("enter syn1");
        syn2();
    }
    
    private synchronized void syn2(){
        System.out.println("enter syn2");
    }
    
    @Override
    public void run() {
        syn1();
    }
}

情況3:證明可重入不要求是同一個(gè)類中的方法:子類調(diào)用父類的方法

package synchornized;

/**
 * Synchronized 可重入粒度測(cè)試:
 * 證明可重入不要求是同一個(gè)類中的方法:子類調(diào)用父類的方法
 */
public class SynchronizedRecursionSuperClass12 {
    public synchronized void syn2(){
        System.out.println("我是父類");
    }
}
class SynchronizedRecursion12 extends SynchronizedRecursionSuperClass12 implements Runnable{
    
    public static void main(String[] args) throws InterruptedException {
        
        SynchronizedRecursion12 runnable = new SynchronizedRecursion12();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    @Override
    public synchronized void syn2(){
        System.out.println("我是子類");
        super.syn2();
    }
    
    @Override
    public void run() {
        syn2();
    }
}

synchornized關(guān)鍵字的粒度肯定不是調(diào)用層面的,是線程級(jí)別的。在同一個(gè)線程中,它如果已經(jīng)拿到了一把鎖,然后又想接著使用這把鎖去訪問其他方法,只要該方法需要的鎖依然是我手中的這把鎖,那么它的可重入的性質(zhì)就會(huì)被激發(fā)出來,就不需要顯式地釋放鎖再重新獲取鎖,它可以直接拿著鎖去完成訪問。

二,不可中斷
一旦這個(gè)鎖已經(jīng)被別人獲得了,如果我還想獲得,我只能選擇等待或者阻塞,直到別的線程釋放這個(gè)鎖。如果別人永遠(yuǎn)不釋放該鎖,那么我只能永遠(yuǎn)地等待下去。
相比之下,在未來會(huì)介紹的Lock類,可以擁有中斷的能力,第一點(diǎn),如果我覺得我等的時(shí)間太長(zhǎng)了,有權(quán)中斷現(xiàn)在已經(jīng)獲取到鎖的線程的執(zhí)行;第二點(diǎn),如果我覺得我等待的時(shí)間太長(zhǎng)了不想再繼續(xù)等了,也可以退出。
Lock相對(duì)于Synchronized更加靈活,但是在編碼的時(shí)候注意點(diǎn)也更多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容