亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文了解Java讀寫鎖ReentrantReadWriteLock的使用

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

概述

ReentrantReadWriteLock不知道大家熟悉嗎?其實在實際的項目中用的比較少,反正我所在的項目沒有用到過。

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

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

ReentrantReadWriteLock介紹

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

讀寫鎖機制:

 讀鎖寫鎖
讀鎖共享互斥
寫鎖互斥互斥

線程進入讀鎖的前提條件:

  • 沒有其他線程的寫鎖
  • 沒有寫請求,或者有寫請求但調(diào)用線程和持有鎖的線程是同一個線程

進入寫鎖的前提條件:

  • 沒有其他線程的讀鎖
  • 沒有其他線程的寫鎖

鎖升級、降級機制:

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

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

  • 鎖升級:從讀鎖變成寫鎖。
  • 鎖降級:從寫鎖變成讀鎖;

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

重入時鎖降級支持:持有寫鎖的情況下去獲取讀鎖,造成只有當(dāng)前線程會持有讀鎖,因為寫鎖會互斥其他的鎖

API介紹

構(gòu)造方法:

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

常用API:

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

代碼范式

加解鎖格式

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

鎖降級

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

實戰(zhà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();
    }

運行結(jié)果:

兩個線程同時運行,都獲取到了讀鎖

驗證讀寫互斥模式

@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();
    }

運行結(jié)果:

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

真實緩存例子

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

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

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

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

public class GenericCachedDao {

    // 緩存對象,這里用jvm緩存
    Map<String, String> cache = new HashMap<>();
    // 讀寫鎖
    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();
        }

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

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

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

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

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

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

相關(guān)文章

  • Java中l(wèi)ist.foreach不能使用字符串拼接的問題

    Java中l(wèi)ist.foreach不能使用字符串拼接的問題

    這篇文章主要介紹了Java中l(wèi)ist.foreach不能使用字符串拼接的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot使用hutool操作FTP的詳細過程

    SpringBoot使用hutool操作FTP的詳細過程

    在使用SpringBoot結(jié)合hutool操作FTP時,遇到防火墻導(dǎo)致上傳文件大小為0kb的問題,通過設(shè)置FTP為被動模式解決,本文詳細解析了FTP的主動模式和被動模式的工作原理、安全性及適用場景,幫助理解FTP的連接方式和解決網(wǎng)絡(luò)限制問題
    2024-09-09
  • 淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法

    淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法

    下面小編就為大家?guī)硪黄獪\析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • 深入理解Spring Aop的執(zhí)行順序

    深入理解Spring Aop的執(zhí)行順序

    本文將結(jié)合實例代碼,介紹Spring Aop的執(zhí)行順序,非常具有實用價值,需要的朋友可以參考下
    2021-06-06
  • Springboot+AOP實現(xiàn)返回數(shù)據(jù)提示語國際化的示例代碼

    Springboot+AOP實現(xiàn)返回數(shù)據(jù)提示語國際化的示例代碼

    這篇文章主要介紹了Springboot+AOP實現(xiàn)返回數(shù)據(jù)提示語國際化的示例代碼,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-07-07
  • IDEA將Maven項目中指定文件夾下的xml等文件編譯進classes的方法

    IDEA將Maven項目中指定文件夾下的xml等文件編譯進classes的方法

    這篇文章主要介紹了IDEA將Maven項目中指定文件夾下的xml等文件編譯進classes的方法,幫助大家更好的利用IDEA進行Java的開發(fā)學(xué)習(xí),感興趣的朋友可以了解下
    2021-01-01
  • Java 可視化垃圾回收_動力節(jié)點Java學(xué)院整理

    Java 可視化垃圾回收_動力節(jié)點Java學(xué)院整理

    Ben Evans是一名資深培訓(xùn)師兼顧問,他在演講可視化垃圾回收中從基礎(chǔ)談起討論了垃圾回收。以下是對其演講的簡短總結(jié)。感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05
  • java 對象的序列化和反序列化詳細介紹

    java 對象的序列化和反序列化詳細介紹

    這篇文章主要介紹了java 對象的序列化和反序列化的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • java爬蟲Gecco工具抓取新聞實例

    java爬蟲Gecco工具抓取新聞實例

    本篇文章主要介紹了JAVA 爬蟲Gecco工具抓取新聞實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2016-10-10
  • Struts2相關(guān)的面試題整理分享

    Struts2相關(guān)的面試題整理分享

    這篇文章主要給大家總結(jié)整理了關(guān)于Struts2相關(guān)的面試題,文中先詳細介紹了關(guān)于struts2的工作原理、工作流程、攔截器和過濾器的區(qū)別以及什么要使用Struts2,然后分享了總結(jié)的一些關(guān)于Struts2面試的一些問題,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-11-11

最新評論