第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

鎖的可重入性驗(yàn)證

1. 前言

本節(jié)內(nèi)容主要是對(duì) Java 鎖的可重入性進(jìn)行驗(yàn)證,鎖的可重入性的設(shè)計(jì)是避免死鎖非常好的設(shè)計(jì)思想。本節(jié)內(nèi)容的知識(shí)點(diǎn)如下:

  • 什么是鎖的可重入性,這是本節(jié)課程的基礎(chǔ)內(nèi)容;
  • 了解可重入鎖與非可重入性鎖的不同之處,以凸顯可重入性鎖的優(yōu)勢(shì)所在,為本節(jié)基礎(chǔ)內(nèi)容;
  • 了解什么情況下使用可重入鎖,是本節(jié)的重點(diǎn)內(nèi)容之一;
  • synchronized 關(guān)鍵字驗(yàn)證鎖的可重入性試驗(yàn),為本節(jié)核心內(nèi)容之一;
  • ReentrantLock 驗(yàn)證鎖的可重入性試驗(yàn),為本節(jié)核心內(nèi)容之一;

其實(shí) synchronized 關(guān)鍵字與 ReentrantLock 都是 Java 常見的可重入鎖,本節(jié)內(nèi)容使用 ReentrantLock 和 synchronized 來講解鎖的可重入性。

2. 什么是鎖的可重入性

定義:可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,再進(jìn)入該線程的內(nèi)層方法會(huì)自動(dòng)獲取鎖(前提鎖對(duì)象得是同一個(gè)對(duì)象或者 class),不會(huì)因?yàn)橹耙呀?jīng)獲取過還沒釋放而阻塞。

Java 中 ReentrantLock 和 synchronized 都是可重入鎖,可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖。

可重入鎖原理:可重入鎖的原理是在鎖內(nèi)部維護(hù)一個(gè)線程標(biāo)示,用來標(biāo)示該鎖目前被哪個(gè)線程占用,然后關(guān)聯(lián)一個(gè)計(jì)數(shù)器。一開始計(jì)數(shù)器值為 0,說明該鎖沒有被任何線程占用。當(dāng)一個(gè)線程獲取了該鎖時(shí),計(jì)數(shù)器的值會(huì)變成 1,這時(shí)其他線程再來獲取該鎖時(shí)會(huì)發(fā)現(xiàn)鎖的所有者不是自己而被阻塞掛起。

但是當(dāng)獲取了該鎖的線程再次獲取鎖時(shí)發(fā)現(xiàn)鎖擁有者是自己,就會(huì)把計(jì)數(shù)器值加+1, 當(dāng)釋放鎖后計(jì)數(shù)器值-1。當(dāng)計(jì)數(shù)器值為 0 時(shí),鎖里面的線程標(biāo)示被重置為 null,這時(shí)候被阻塞的線程會(huì)被喚醒來競(jìng)爭(zhēng)獲取該鎖。

3. 可重入鎖與非可重入性鎖

Java 中 ReentrantLock 和 synchronized 都是可重入鎖,可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖。

為了解釋可重入鎖與非可重入性鎖的區(qū)別與聯(lián)系,我們拿可重入鎖 ReentrantLock 和 非重入鎖 NonReentrantLock 進(jìn)行簡(jiǎn)單的分析對(duì)比。

相同點(diǎn): ReentrantLock 和 NonReentrantLock 都繼承父類 AQS,其父類 AQS 中維護(hù)了一個(gè)同步狀態(tài) status 來計(jì)數(shù)重入次數(shù),status 初始值為 0。

不同點(diǎn):當(dāng)線程嘗試獲取鎖時(shí),可重入鎖先嘗試獲取并更新 status 值,如果 status == 0 表示沒有其他線程在執(zhí)行同步代碼,則把 status 置為 1,當(dāng)前線程開始執(zhí)行。

如果 status != 0,則判斷當(dāng)前線程是否是獲取到這個(gè)鎖的線程,如果是的話執(zhí)行 status+1,且當(dāng)前線程可以再次獲取鎖。

而非可重入鎖是直接去獲取并嘗試更新當(dāng)前 status 的值,如果 status != 0 的話會(huì)導(dǎo)致其獲取鎖失敗,當(dāng)前線程阻塞,導(dǎo)致死鎖發(fā)生。

4. 什么情況下使用可重入鎖

我們先來看看如下代碼:同步方法 helloB 方法調(diào)用了同步方法 helloA。

public class DemoTest{
    public synchronized void helloA(){
        System.out.println("helloA");
    }
    public synchronized void helloB(){
        System.out.println("helloB");
        helloA();
    }
}

在如上代碼中,調(diào)用 helloB 方法前會(huì)先獲取內(nèi)置鎖,然后打印輸出。之后調(diào)用 helloA 方法,在調(diào)用前會(huì)先去獲取內(nèi)置鎖,如果內(nèi)置鎖不是可重入的,那么調(diào)用線程將會(huì)一直被阻塞。

因此,對(duì)于同步方法內(nèi)部調(diào)用另外一個(gè)同步方法的情況下,一定要使用可重入鎖,不然會(huì)導(dǎo)致死鎖的發(fā)生。

5. synchronized 驗(yàn)證鎖的可重入性

為了更好的理解 synchronized 驗(yàn)證鎖的可重入性,我們來設(shè)計(jì)一個(gè)簡(jiǎn)單的場(chǎng)景。

場(chǎng)景設(shè)計(jì)

  • 創(chuàng)建一個(gè)類,該類中有兩個(gè)方法,helloA 方法和 helloB 方法;
  • 將兩個(gè)方法內(nèi)部的邏輯進(jìn)行 synchronized 同步;
  • helloA 方法內(nèi)部調(diào)用 helloB 方法,營(yíng)造可重入鎖的場(chǎng)景;
  • main 方法創(chuàng)建線程,調(diào)用 helloA 方法;
  • 觀察結(jié)果,看是否可以成功進(jìn)行調(diào)用。

實(shí)例

public class DemoTest {
    public static void main(String[] args) {
        new Thread(new SynchronizedTest()). start();
    }
}
class SynchronizedTest implements Runnable {
    private final Object obj = new Object();
    public void helloA() { //方法1,調(diào)用方法2
        synchronized (obj) {
            System.out.println(Thread.currentThread().getName() + " helloA()");
            helloB();
        }
    }
    public void helloB() {
        synchronized (obj) {
            System.out.println(Thread.currentThread().getName() + " helloB()");
        }
    }
    @Override
    public void run() {
        helloA(); //調(diào)用helloA方法
    }
}

結(jié)果驗(yàn)證

Thread-0 helloA()
Thread-0 helloB()

結(jié)果解析:如果同一線程,鎖不可重入的話,helloB 需要等待 helloA 釋放 obj 鎖,如此一來,helloB 無法進(jìn)行鎖的獲取,最終造成無限等待,無法正常執(zhí)行。此處說明了 synchronized 關(guān)鍵字的可重入性,因此能夠正常進(jìn)行兩個(gè)方法的執(zhí)行。

6. ReentrantLock 驗(yàn)證鎖的可重入性

相同的場(chǎng)景,對(duì)代碼進(jìn)行如下改造,將 synchronized 同步代碼塊修改成 lock 接口同步,我們看代碼實(shí)例如下:

public class DemoTest {
    public static void main(String[] args) {
        new Thread(new SynchronizedTest()). start();
    }
}
class SynchronizedTest implements Runnable {
    private final Lock lock = new ReentrantLock();
    public void helloA() { //方法1,調(diào)用方法2
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " helloA()");
            helloB();
        } finally {
            lock.unlock();
        }
    }
    public void helloB() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " helloB()");
        } finally {
            lock.unlock();
        }
    }
    @Override
    public void run() {
        helloA();
    }
}

結(jié)果驗(yàn)證

Thread-0 helloA()
Thread-0 helloB()

結(jié)果解析:ReentrantLock 一樣是可重入鎖,試驗(yàn)成功。

7. 小結(jié)

鎖的可重入性這一概念對(duì)于并發(fā)編程非常重要,對(duì)于本節(jié)內(nèi)容需要深入的理解并掌握。我們之前已經(jīng)學(xué)習(xí)過了 synchronized 關(guān)鍵字和 ReentrantLock 鎖,此處知識(shí)用兩者進(jìn)行了可重入的驗(yàn)證。

本節(jié)關(guān)鍵點(diǎn)在于可重入性的意義所在,需要結(jié)合實(shí)例進(jìn)行更加細(xì)致的理解和掌握。