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

Java中ReentrantReadWriteLock讀寫(xiě)鎖的實(shí)現(xiàn)

 更新時(shí)間:2025年05月23日 08:33:49   作者:Volunteer Technology  
本文主要介紹了ReentrantReadWriteLock讀寫(xiě)鎖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、鎖的分類(lèi)

這里不會(huì)對(duì)Java中大部分的分類(lèi)都聊清楚,主要把 **互斥,共享** 這種分類(lèi)聊清楚。

Java中的互斥鎖,synchronized,ReentrantLock這種都是互斥鎖。一個(gè)線程持有鎖操作時(shí),其他線程都需要等待前面的線程釋放鎖資源,才能重新嘗試競(jìng)爭(zhēng)這把鎖。

Java中的讀寫(xiě)鎖(支撐互斥&共享),Java中最常見(jiàn)的就是 **ReentrantReadWriteLock** ,StampedLock。

其中StampedLock是JDK1.8中推出的一款讀寫(xiě)鎖的實(shí)現(xiàn),針對(duì)ReentrantReadWriteLock一個(gè)優(yōu)化。但是,今兒不細(xì)聊。主要玩ReentrantReadWriteLock。

ReentrantReadWriteLock主要就是解決咱們剛才聊的,讀寫(xiě)操作都有,讀操作居多,寫(xiě)操作頻次相對(duì)比較低的情況,可以使用讀寫(xiě)鎖來(lái)提升系統(tǒng)性能。

讀寫(xiě)鎖中:

* 寫(xiě)寫(xiě)互斥
* 讀寫(xiě)互斥
* 寫(xiě)讀互斥
* 讀讀共享
* 有鎖降級(jí)的情況,后面聊??!

二、ReentrantReadWriteLock的基本操作

ReentrantReadWriteLock中實(shí)現(xiàn)了ReadWriteLock的接口,在這個(gè)接口里面提供了兩個(gè)抽象方法。

正常的操作,是new ReentrantReadWriteLock的對(duì)象,但是你具體的業(yè)務(wù)操作是需要讀鎖,還是寫(xiě)鎖,你需要單獨(dú)的獲取到,然后針對(duì)性的加鎖。

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

具體使用方式

public static void main(String[] args){
    // 1、構(gòu)建讀寫(xiě)鎖對(duì)象
    ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 2、單獨(dú)獲取讀、寫(xiě)鎖對(duì)象
    ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

    // 3、根據(jù)業(yè)務(wù)使用具體的鎖對(duì)象加鎖
    writeLock.lock();
    // try-finally的目的,是為了避免沒(méi)有及時(shí)釋放鎖資源導(dǎo)致死鎖的問(wèn)題。
    try{
        // 4、業(yè)務(wù)操作…………
        System.out.println("寫(xiě)操作");
    }finally {
        // 5、釋放鎖
        writeLock.unlock();
    }
}

三、ReentrantReadWriteLock的底層實(shí)現(xiàn)

ReentrantReadWriteLock是基于AQS實(shí)現(xiàn)的。

AQS是JUC包下的一個(gè)抽象類(lèi)AbstractQueuedSynchronizer

暫時(shí)只關(guān)注兩點(diǎn),分別是AQS提供的state屬性,還有AQS提供的一個(gè)同步隊(duì)列。

state屬性,用來(lái)標(biāo)識(shí)當(dāng)前 讀寫(xiě)鎖 的資源是否被占用的核心標(biāo)識(shí)。
private volatile int state;

一個(gè)int類(lèi)型的state,是4字節(jié),每個(gè)字節(jié)占用8個(gè)bit位,一個(gè)state占用32個(gè)bit位。

* 高16位,作為讀鎖的標(biāo)記。
* 低16位,作為寫(xiě)鎖的標(biāo)記。

static final int SHARED_SHIFT   = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
00000000 00000000 11111111 11111111
/** 查看讀鎖的占用情況。 */
static int sharedCount(int state)    { return state >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count  */
static int exclusiveCount(int state) { return state & EXCLUSIVE_MASK; }

00000000 00000000 00000000 00000000    int類(lèi)型的數(shù)值的32個(gè)bit位。 

讀鎖占用情況:
00000000 00000011 00000000 00000000    state
>>> 16 
00000000 00000000 00000000 00000011    讀鎖被獲取了三次。

寫(xiě)鎖占用情況。(這里之所以&這個(gè)么東西,是對(duì)后期的鎖降級(jí)有影響~)
00000000 00000000 00000000 00000001    state
&
00000000 00000000 11111111 11111111  
=
00000000 00000000 00000000 00000001    寫(xiě)鎖被獲取了一次。

一個(gè)同步隊(duì)列,當(dāng)線程獲取鎖資源失敗時(shí),需要到這個(gè)同步隊(duì)列中排隊(duì)。到了合適的時(shí)機(jī),就會(huì)繼續(xù)嘗試獲取對(duì)應(yīng)的鎖資源。

四、ReentrantReadWriteLock的鎖重入

同一個(gè)線程,多次獲取同一把鎖時(shí),就會(huì)出現(xiàn)鎖重入的情況。

而咱們大多數(shù)的鎖,都會(huì)提供鎖重入的功能。

鎖重入場(chǎng)景:

public class Demo {
    // 1、構(gòu)建讀寫(xiě)鎖對(duì)象
    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 2、單獨(dú)獲取讀、寫(xiě)鎖對(duì)象
    static ReentrantReadWriteLock.ReadLock readLock;
    static ReentrantReadWriteLock.WriteLock writeLock;

    static{
        // 2、單獨(dú)獲取讀、寫(xiě)鎖對(duì)象
        readLock = readWriteLock.readLock();
        writeLock = readWriteLock.writeLock();
    }

    public static void main(String[] args){
        // 3、根據(jù)業(yè)務(wù)使用具體的鎖對(duì)象加鎖
        writeLock.lock();
        // try-finally的目的,是為了避免沒(méi)有及時(shí)釋放鎖資源導(dǎo)致死鎖的問(wèn)題。
        try{
            // 4、業(yè)務(wù)操作…………調(diào)用其他方法
            xxx();
        }finally {
            // 5、釋放鎖
            writeLock.unlock();
        }
    }

    private static void xxx(){
        writeLock.lock();
        try{
            // 其他按業(yè)務(wù)
        }finally {
            writeLock.unlock();
        }
    }
}

咱們底層的鎖重入邏輯很簡(jiǎn)單

**寫(xiě)鎖:** 寫(xiě)鎖的實(shí)現(xiàn)就是每一次獲取寫(xiě)鎖時(shí),會(huì)對(duì)state的低16位+1,再次獲取,再次+1。同理,每次釋放鎖資源時(shí),也需要對(duì)state進(jìn)行-1。 而當(dāng)對(duì)state的低16位減到0時(shí),鎖資源就釋放干凈了。

讀鎖: 首先,讀鎖是共享的,他用state的高16位來(lái)維護(hù)信息。如果高16位的state的值,經(jīng)過(guò)運(yùn)算,知道了是4,也就是讀鎖被獲取了4次??赡蹵線程獲取了2次讀鎖資源。 B線程獲取了2次讀鎖資源。高位的state自然就是4。但是因?yàn)槌绦騿T寫(xiě)代碼除了問(wèn)題,使用A線程,釋放了4次讀鎖資源,那此時(shí)B線程是不是就可能出現(xiàn)數(shù)據(jù)安全問(wèn)題了。

所以,為了解決上述的問(wèn)題,每個(gè)線程需要獨(dú)立的記錄自己獲取了幾次讀鎖資源??梢允褂肨hreadLocal來(lái)保存線程局部的信息,每次加鎖時(shí),ThreadLocal中需要存儲(chǔ)一個(gè)標(biāo)記,每次+1。每次釋放鎖時(shí),也需要將ThreadLocal中的標(biāo)記進(jìn)行-1。讀線程最后是基于自己的ThreadLocal中的數(shù)值,來(lái)確認(rèn)讀鎖是否釋放干凈。

五、ReentrantReadWriteLock的寫(xiě)鎖饑餓

寫(xiě)鎖饑餓的問(wèn)題。

如果寫(xiě)線程在AQS中排隊(duì),并且排在head.next的位置。 那么其他想獲取讀鎖的讀線程需要排隊(duì)。避免大量的讀請(qǐng)求獲取讀鎖,讓寫(xiě)線程一直AQS隊(duì)列中排隊(duì),無(wú)法執(zhí)行寫(xiě)操作的問(wèn)題。

通過(guò)源碼可以看到,讀寫(xiě)鎖中,僅僅針對(duì)head.next這個(gè)節(jié)點(diǎn)的情況,來(lái)確認(rèn)讀線程獲取讀鎖時(shí)是否需要排隊(duì)

// 這個(gè)方法,總結(jié)一句話。  
// AQS中有排隊(duì)的Node,并且head的next節(jié)點(diǎn)是一個(gè)有線程并且在等待寫(xiě)鎖的Node
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}

ReentrantReadWriteLock讀寫(xiě)鎖中有鎖降級(jí),但是這個(gè)和synchronized的鎖升級(jí)沒(méi)任何關(guān)系?。?!

六、ReentrantReadWriteLock的鎖降級(jí)

ReentrantReadWriteLock的鎖降級(jí)是指當(dāng)前線程如果持有了寫(xiě)鎖,可以降級(jí)直接獲取到讀鎖。

在讀寫(xiě)鎖中,持有寫(xiě)鎖的同時(shí),再去獲取讀鎖,這種行為一般被稱(chēng)為 **鎖降級(jí)** 。

在讀寫(xiě)鎖中,持有讀鎖的同時(shí),去獲取寫(xiě)鎖,這種行為被稱(chēng)為 **鎖升級(jí)** ,這個(gè)行為是不允許的。

這里是獲取讀鎖的的邏輯,看一下鎖降級(jí)的支持方式

// 競(jìng)爭(zhēng)讀鎖。
if (exclusiveCount(c) != 0 &&   // 這行代表某個(gè)線程持有寫(xiě)鎖
    getExclusiveOwnerThread() != current)    // 這行代表持有寫(xiě)鎖的不是當(dāng)前線程
    // 退出競(jìng)爭(zhēng),無(wú)法獲取讀鎖
    return -1;  

前面邏輯沒(méi)有走return - 1之后,在后續(xù)就會(huì)正常的對(duì)state的高位+1,并且完成讀鎖的計(jì)數(shù)操作。

七、ReentrantReadWriteLock的優(yōu)化 

ReentrantReadWriteLock的優(yōu)化主要是在讀鎖計(jì)數(shù)層面上做的優(yōu)化。

這個(gè)對(duì)性能的優(yōu)化微乎其微,但是確確實(shí)實(shí)是一個(gè)優(yōu)化。

在獲取讀鎖時(shí),因?yàn)槭枪蚕淼模@種優(yōu)化只針對(duì)第一個(gè)獲取讀鎖的線程和最后一個(gè)獲取讀鎖的線程。

針對(duì)第一個(gè)獲取讀鎖的線程,他采用一個(gè)全局變量記錄重入次數(shù)。這個(gè)操作可以節(jié)省掉使用ThreadLocal的時(shí)間成本和內(nèi)存成本。

其中firstReader記錄第一個(gè)獲取讀鎖的線程。

firstReaderHoldCount,記錄第一個(gè)獲取讀鎖的線程的重入次數(shù)。

這里是最后一個(gè)獲取讀鎖的線程需要走的邏輯

cachedHoldCounter這個(gè)屬性是記錄最后一個(gè)獲取讀鎖的線程的重入次數(shù)。

這里可以讓最后一個(gè)獲取讀鎖的線程在重入時(shí),省略掉去ThreadLocal中g(shù)et計(jì)數(shù)器的操作,但是之前的set存儲(chǔ)操作,不能省略

// 獲取上次最后獲取讀鎖的線程
HoldCounter rh = cachedHoldCounter;
// 查看當(dāng)前線程是否是之前的cachedHoldCounter
if (rh == null || rh.tid != getThreadId(current))
    // 說(shuō)明不是,將當(dāng)前獲取讀鎖的線程設(shè)置為cachedHoldCounter
    cachedHoldCounter = rh = readHolds.get();
// 這個(gè)判斷代表第一次獲取讀鎖才會(huì)進(jìn)去
else if (rh.count == 0)
    // 如果是第一次獲取讀鎖,不是重入,還是需要扔到ThreadLocal里紀(jì)錄好,。
    readHolds.set(rh);
// 直接對(duì)獲取到的rh做++操作,代表獲取了一次讀鎖。
rh.count++;

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

相關(guān)文章

  • 如何在Spring Boot應(yīng)用中優(yōu)雅的使用Date和LocalDateTime的教程詳解

    如何在Spring Boot應(yīng)用中優(yōu)雅的使用Date和LocalDateTime的教程詳解

    這篇文章主要介紹了如何在Spring Boot應(yīng)用中優(yōu)雅的使用Date和LocalDateTime,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • java中toString()、String.valueOf()、(String)?強(qiáng)轉(zhuǎn)的區(qū)別

    java中toString()、String.valueOf()、(String)?強(qiáng)轉(zhuǎn)的區(qū)別

    在實(shí)際開(kāi)發(fā)中,少不了使用這三種方法對(duì)某一個(gè)類(lèi)型的數(shù)據(jù)進(jìn)行轉(zhuǎn)?String?的操作,本文就來(lái)介紹了java中toString()、String.valueOf()、(String)?強(qiáng)轉(zhuǎn)的區(qū)別,感興趣的可以了解一下
    2024-06-06
  • SpringBoot yml配置文件調(diào)用過(guò)程解析

    SpringBoot yml配置文件調(diào)用過(guò)程解析

    這篇文章主要介紹了SpringBoot yml配置文件調(diào)用過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • mapstruct中的@Mapper注解的基本用法

    mapstruct中的@Mapper注解的基本用法

    在MapStruct中,@Mapper注解是核心注解之一,用于標(biāo)記一個(gè)接口或抽象類(lèi)為MapStruct的映射器(Mapper),本文給大家介紹mapstruct中的@Mapper注解的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2025-06-06
  • Spring Framework中JDBC批量操作的三種實(shí)現(xiàn)方式

    Spring Framework中JDBC批量操作的三種實(shí)現(xiàn)方式

    本文詳細(xì)介紹了如何使用 Spring 的 JdbcTemplate 進(jìn)行高效的數(shù)據(jù)庫(kù)批量更新,從而減少與數(shù)據(jù)庫(kù)之間的網(wǎng)絡(luò)往返次數(shù)(round-trips),提升性能,下面我將用通俗易懂的方式,結(jié)合代碼示例和實(shí)際場(chǎng)景給大家詳細(xì)說(shuō)說(shuō),需要的朋友可以參考下
    2025-10-10
  • Java-性能分析和監(jiān)控工具深入詳解

    Java-性能分析和監(jiān)控工具深入詳解

    這篇文章主要介紹了Java-性能分析和監(jiān)控工具深入詳解,文章內(nèi)容詳細(xì),簡(jiǎn)單易懂,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2023-01-01
  • SpringBoot中多環(huán)境yml的配置與打包問(wèn)題

    SpringBoot中多環(huán)境yml的配置與打包問(wèn)題

    這篇文章主要介紹了SpringBoot中多環(huán)境yml的配置與打包問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 淺談hibernate急迫加載問(wèn)題(多重外鍵關(guān)聯(lián))

    淺談hibernate急迫加載問(wèn)題(多重外鍵關(guān)聯(lián))

    這篇文章主要介紹了淺談hibernate急迫加載問(wèn)題(多重外鍵關(guān)聯(lián)),具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • java如何使用fastjson修改多層嵌套的Objectjson數(shù)據(jù)

    java如何使用fastjson修改多層嵌套的Objectjson數(shù)據(jù)

    這篇文章主要介紹了java如何使用fastjson修改多層嵌套的Objectjson數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java接入DeepSeek的保姆級(jí)教程(適合新手)

    Java接入DeepSeek的保姆級(jí)教程(適合新手)

    對(duì)于目前的DeepSeek大家應(yīng)該都不是很陌生,目前也是最流行的一款A(yù)I軟件了,所以為了讓我們開(kāi)發(fā)更全面,能夠在自己的項(xiàng)目中融入AI那就會(huì)很全面了,所以這次的文章,將模擬一個(gè)基礎(chǔ)案例,可以在這個(gè)基礎(chǔ)案例迭代實(shí)現(xiàn)出你自己的AI,需要的朋友可以參考下
    2025-02-02

最新評(píng)論

特克斯县| 体育| 九龙城区| 衢州市| 项城市| 苍溪县| 茶陵县| 左云县| 综艺| 翼城县| 汤原县| 浦县| 墨江| 涿鹿县| 嵊泗县| 临猗县| 开封县| 潮安县| 当雄县| 洱源县| 铜梁县| 涪陵区| 玉林市| 苏州市| 略阳县| 巴中市| 贵州省| 淮滨县| 兴国县| 新邵县| 高青县| 留坝县| 望城县| 奇台县| 乌兰县| 思南县| 清徐县| 宁都县| 广元市| 工布江达县| 望城县|