最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文了解Java讀寫(xiě)鎖ReentrantReadWriteLock的使用

 更新時(shí)間:2022年10月16日 14:15:38   作者:JAVA旭陽(yáng)  
ReentrantReadWriteLock稱(chēng)為讀寫(xiě)鎖,它提供一個(gè)讀鎖,支持多個(gè)線程共享同一把鎖。這篇文章主要講解一下ReentrantReadWriteLock的使用和應(yīng)用場(chǎng)景,感興趣的可以了解一下

概述

ReentrantReadWriteLock不知道大家熟悉嗎?其實(shí)在實(shí)際的項(xiàng)目中用的比較少,反正我所在的項(xiàng)目沒(méi)有用到過(guò)。

ReentrantReadWriteLock稱(chēng)為讀寫(xiě)鎖,它提供一個(gè)讀鎖,支持多個(gè)線程共享同一把鎖。它也提供了一把寫(xiě)鎖,是獨(dú)占鎖,和其他讀鎖或者寫(xiě)鎖互斥,表明只有一個(gè)線程能持有鎖資源。通過(guò)兩把鎖的協(xié)同工作,能夠最大化的提高讀寫(xiě)的性能,特別是讀多寫(xiě)少的場(chǎng)景,而往往大部分的場(chǎng)景都是讀多寫(xiě)少的。

本文主要講解ReentrantReadWriteLock的使用和應(yīng)用場(chǎng)景。

ReentrantReadWriteLock介紹

ReentrantReadWriteLock實(shí)現(xiàn)了ReadWriteLock接口,可以獲取到讀鎖(共享鎖),寫(xiě)鎖(獨(dú)占鎖)。同時(shí),通過(guò)構(gòu)造方法可以創(chuàng)建鎖本身是公平鎖還是非公鎖。

讀寫(xiě)鎖機(jī)制:

 讀鎖寫(xiě)鎖
讀鎖共享互斥
寫(xiě)鎖互斥互斥

線程進(jìn)入讀鎖的前提條件:

  • 沒(méi)有其他線程的寫(xiě)鎖
  • 沒(méi)有寫(xiě)請(qǐng)求,或者有寫(xiě)請(qǐng)求但調(diào)用線程和持有鎖的線程是同一個(gè)線程

進(jìn)入寫(xiě)鎖的前提條件:

  • 沒(méi)有其他線程的讀鎖
  • 沒(méi)有其他線程的寫(xiě)鎖

鎖升級(jí)、降級(jí)機(jī)制:

我們知道ReentrantLock具備可重入的能力,即同一個(gè)線程多次獲取鎖,不引起阻塞,那么ReentrantReadWriteLock關(guān)于可重入性是怎么樣的呢?

關(guān)于這個(gè)問(wèn)題需要引入兩個(gè)概念,鎖升級(jí),鎖降級(jí)。

  • 鎖升級(jí):從讀鎖變成寫(xiě)鎖。
  • 鎖降級(jí):從寫(xiě)鎖變成讀鎖;

重入時(shí)鎖升級(jí)不支持:持有讀鎖的情況下去獲取寫(xiě)鎖會(huì)導(dǎo)致獲取寫(xiě)鎖永久等待,需要先釋放讀,再去獲得寫(xiě)

重入時(shí)鎖降級(jí)支持:持有寫(xiě)鎖的情況下去獲取讀鎖,造成只有當(dāng)前線程會(huì)持有讀鎖,因?yàn)閷?xiě)鎖會(huì)互斥其他的鎖

API介紹

構(gòu)造方法:

  • public ReentrantReadWriteLock():默認(rèn)構(gòu)造方法,非公平鎖
  • public ReentrantReadWriteLock(boolean fair):true 為公平鎖

常用API:

  • public ReentrantReadWriteLock.ReadLock readLock():返回讀鎖
  • public ReentrantReadWriteLock.WriteLock writeLock():返回寫(xiě)鎖
  • public void lock():加鎖
  • public void unlock():解鎖
  • public boolean tryLock():嘗試獲取鎖

代碼范式

加解鎖格式

r.lock();
try {
    // 臨界區(qū)
} finally {
	r.unlock();
}

鎖降級(jí)

w.lock();
try {
    r.lock();// 降級(jí)為讀鎖, 釋放寫(xiě)鎖, 這樣能夠讓其它線程讀取緩存
    try {
        // ...
    } finally{
    	w.unlock();// 要在寫(xiě)鎖釋放之前獲取讀鎖
    }
} finally{
	r.unlock();
}

實(shí)戰(zhàn)案例

驗(yàn)證讀讀共享模式

@Test
    public void readReadMode() throws InterruptedException {
        ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock r = rw.readLock();
        ReentrantReadWriteLock.WriteLock w = rw.writeLock();

        Thread thread0 = new Thread(() -> {
            r.lock();
            try {
                Thread.sleep(1000);
                System.out.println("Thread 1 running " + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                r.unlock();
            }
        },"t1");

        Thread thread1 = new Thread(() -> {
            r.lock();
            try {
                Thread.sleep(1000);
                System.out.println("Thread 2 running " + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                r.unlock();
            }
        },"t2");

        thread0.start();
        thread1.start();

        thread0.join();
        thread1.join();
    }

運(yùn)行結(jié)果:

兩個(gè)線程同時(shí)運(yùn)行,都獲取到了讀鎖

驗(yàn)證讀寫(xiě)互斥模式

@Test
    public void readWriteMode() throws InterruptedException {
        ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock r = rw.readLock();
        ReentrantReadWriteLock.WriteLock w = rw.writeLock();

        Thread thread0 = new Thread(() -> {
            r.lock();
            try {
                Thread.sleep(1000);
                System.out.println("Thread 1 running " + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                r.unlock();
            }
        },"t1");

        Thread thread1 = new Thread(() -> {
            w.lock();
            try {
                Thread.sleep(1000);
                System.out.println("Thread 2 running " + new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                w.unlock();
            }
        },"t2");

        thread0.start();
        thread1.start();

        thread0.join();
        thread1.join();
    }

運(yùn)行結(jié)果:

兩個(gè)線程間隔1秒,互斥執(zhí)行

真實(shí)緩存例子

什么場(chǎng)景下讀多寫(xiě)少? 想必最先想到的就是緩存把,ReentrantReadWriteLock在緩存場(chǎng)景中就是一個(gè)很典型的應(yīng)用。

緩存更新時(shí),是先清緩存還是先更新數(shù)據(jù)庫(kù)?

  • 先清緩存:可能造成剛清理緩存還沒(méi)有更新數(shù)據(jù)庫(kù),高并發(fā)下,其他線程直接查詢(xún)了數(shù)據(jù)庫(kù)過(guò)期數(shù)據(jù)到緩存中,這種情況非常嚴(yán)重,直接導(dǎo)致后續(xù)所有的請(qǐng)求緩存和數(shù)據(jù)庫(kù)不一致。
  • 先更新?lián)?kù):可能造成剛更新數(shù)據(jù)庫(kù),還沒(méi)清空緩存就有線程從緩存拿到了舊數(shù)據(jù),這種情況概率比較小,影響范圍有限,只對(duì)這一次的查詢(xún)結(jié)果有問(wèn)題。

顯而易見(jiàn),通常情況下,先更新數(shù)據(jù)庫(kù),然后清空緩存。

public class GenericCachedDao {

    // 緩存對(duì)象,這里用jvm緩存
    Map<String, String> cache = new HashMap<>();
    // 讀寫(xiě)鎖
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 讀取操作
    public String getData(String key) {
        // 加讀鎖,防止其他線程修改緩存
        readWriteLock.readLock().lock();
        try {
            String value = cache.get(key);
            // 如果緩存命中,返回
            if(value != null) {
                return value;
            }
        } finally {
            // 釋放讀鎖
            readWriteLock.readLock().unlock();
        }

        //如果緩存沒(méi)有命中,從數(shù)據(jù)庫(kù)中加載
        readWriteLock.writeLock().lock();
        try {
            // 細(xì)節(jié),為防止重復(fù)查詢(xún)數(shù)據(jù)庫(kù), 再次驗(yàn)證
            // 因?yàn)間et 方法上面部分是可能多個(gè)線程進(jìn)來(lái)的, 可能已經(jīng)向緩存填充了數(shù)據(jù)
            String value = cache.get(key);
            if(value == null) {
                // 這里可以改成從數(shù)據(jù)庫(kù)查詢(xún)
                value = "alvin";
                cache.put(key, value);
            }
            return value;
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // 更新數(shù)據(jù)
    public void updateData(String key, String value) {
        // 加寫(xiě)鎖
        readWriteLock.writeLock().lock();
        try {
            // 更新操作TODO

            // 清空緩存
            cache.remove(key);
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
}

getData方法是讀取操作,先加讀鎖,從緩存讀取,如果沒(méi)有命中,加寫(xiě)鎖,此時(shí)其他線程就不能讀取了,等寫(xiě)入成功后,釋放讀鎖。

updateData方法是寫(xiě)操作,更新時(shí)加寫(xiě)鎖,其他線程此時(shí)無(wú)法讀取,然后清空緩存中的舊數(shù)據(jù)。

到此這篇關(guān)于一文了解Java讀寫(xiě)鎖ReentrantReadWriteLock的使用的文章就介紹到這了,更多相關(guān)Java讀寫(xiě)鎖ReentrantReadWriteLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

天等县| 安平县| 开化县| 体育| 绥德县| 肥西县| 平潭县| 屯留县| 镇江市| 鹰潭市| 澄迈县| 武穴市| 安溪县| 双牌县| 汾西县| 平阳县| 宁德市| 莱芜市| 南涧| 乐山市| 镇宁| 海晏县| 沐川县| 大同县| 侯马市| 武邑县| 潮州市| 谢通门县| 资阳市| 景德镇市| 英山县| 仁怀市| 沛县| 宣城市| 陆川县| 神池县| 精河县| 涟源市| 米脂县| 仲巴县| 通化县|