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

Java中的ReadWriteLock讀寫鎖實(shí)例詳解

 更新時(shí)間:2026年03月07日 10:28:53   作者:懶惰蝸牛  
Java中的ReadWriteLock讀寫鎖,它是一種讀寫分離的鎖機(jī)制,適用于讀多寫少的場(chǎng)景,本文通過(guò)實(shí)例代碼給大家介紹Java中的ReadWriteLock讀寫鎖,感興趣的朋友跟隨小編一起看看吧

在之前的文章中,我們已經(jīng)學(xué)習(xí)了synchronized和ReentrantLock。通過(guò)學(xué)習(xí)我們知道這兩者都是屬于排它鎖,也叫做互斥鎖。

什么叫互斥鎖呢?就是不管哪個(gè)線程去讀取數(shù)據(jù)還是修改數(shù)據(jù),只要他持有鎖,那其他的線程就必須等著。

這在大多數(shù)情況下肯定是線程安全的,沒(méi)什么問(wèn)題。

但是在實(shí)際的開(kāi)發(fā)過(guò)程中,有一些很常見(jiàn)的讀多寫少的場(chǎng)景。像配置信息,熱點(diǎn)數(shù)據(jù)的緩存。這些數(shù)據(jù)實(shí)際上被大量的線程進(jìn)行頻繁的讀取,而只在極少的情況下會(huì)去修改。

在這類場(chǎng)景下,如果讀取操作也使用排他鎖,想想都會(huì)降低系統(tǒng)性能。因?yàn)樽x取操作本身并不會(huì)改變數(shù)據(jù),多個(gè)線程同時(shí)讀取同一個(gè)數(shù)據(jù)是完全安全的。

那有沒(méi)有一種鎖能把讀取和寫入?yún)^(qū)別開(kāi)呢?

那肯定是有的,JUC包下的ReadWriteLock就是為解決這個(gè)問(wèn)題而生的。

今天我們就一起來(lái)看看ReadWriteLock。

一、什么是ReadWriteLock

ReadWriteLock是java.util.concurrent.locks中的一個(gè)接口。既然是接口,那他就不是一把具體的鎖。

可以把他理解成一個(gè)鎖的管理者。他內(nèi)部管理了兩把相互關(guān)聯(lián)的鎖:

一個(gè)讀取鎖,也叫做共享鎖。

一個(gè)寫入所,也叫排它鎖。

再提一句,源碼中的注釋真的有必要去看一看,不管是學(xué)習(xí)前還是學(xué)習(xí)后,盡量都去閱讀下源碼及注釋。

ReentrantReadWriteLock是ReadWriteLock接口最常用的實(shí)現(xiàn)類。

這套鎖機(jī)制有以下的核心原則:

讀-讀 共享:多個(gè)線程可以同時(shí)持有讀取鎖,進(jìn)行并發(fā)讀取。

讀-寫 互斥:當(dāng)有線程持有讀取鎖時(shí),寫入線程必須等待。

寫-讀 互斥:當(dāng)有線程持有寫入鎖時(shí),所有其他線程(不管讀寫)都必須等待。

寫-寫 互斥:當(dāng)有線程持有寫入鎖時(shí),其他寫入線程也必須等待。

其實(shí)很好理解,讀鎖大家用,寫鎖只能一個(gè)人用。

大家都是讀,其樂(lè)融融,隨便讀。

我在讀,你想寫,你得等著,等我讀完了,不然就存在邊讀邊寫的詭異事情。

我在寫,你想讀或者你也想寫,那都得等著,不然又會(huì)混亂了。

簡(jiǎn)單的總結(jié)就是:讀鎖大家用,寫鎖我獨(dú)占。

二、讀寫鎖使用

我們通過(guò)一個(gè)小案例來(lái)看一下讀寫鎖的使用:

package com.lazy.snail.day40;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @ClassName Day40Demo
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/8/12 9:52
 * @Version 1.0
 */
public class Day40Demo {
    private final Map<String, String> map = new HashMap<>();
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock rLock = rwLock.readLock();
    private final Lock wLock = rwLock.writeLock();
    public String get(String key) {
        rLock.lock();
        try { return map.get(key); }
        finally { rLock.unlock(); }
    }
    public void put(String key, String value) {
        wLock.lock();
        try { map.put(key, value); }
        finally { wLock.unlock(); }
    }
}

之前的集合框架學(xué)習(xí)中,我們已經(jīng)知道HashMap自身不是線程安全的,但是我們可以用鎖把所有的訪問(wèn)都包起來(lái)。來(lái)保證線程安全。

rwLock是我們創(chuàng)建的讀寫鎖管理器,生成兩把關(guān)聯(lián)的鎖:讀鎖、寫鎖。

rLock和wLock分別取出讀鎖和寫鎖。

get方法代表只讀路徑,也就是讀取。在沒(méi)有線程持有寫鎖/等待寫的時(shí)候,多個(gè)線程可以同時(shí)獲取讀鎖。

在持鎖期間進(jìn)行讀?。╩ap.get()),避免了跟寫線程并發(fā)導(dǎo)致的數(shù)據(jù)競(jìng)爭(zhēng)。

最后在finally里面釋放鎖。

put方法代表寫路徑,也就是寫入。

寫鎖是獨(dú)占的。只要有線程持有寫鎖,其他讀/寫都得等;反過(guò)來(lái),只要有讀鎖在,寫鎖也進(jìn)不來(lái)。

唯一寫入口(map.put),確保不會(huì)出現(xiàn)同時(shí)寫或讀寫交疊。

同樣在finally中釋放鎖。

三、內(nèi)部原理

3.1 鎖實(shí)現(xiàn)及獲取

ReentrantReadWriteLock內(nèi)部并不是維護(hù)兩把完全獨(dú)立的鎖,而是用一個(gè)32位int變量state同時(shí)記錄讀寫狀態(tài):

其中高16位表示讀鎖計(jì)數(shù),允許多個(gè)線程同時(shí)增加。

低16位表示寫鎖計(jì)數(shù),同一個(gè)線程可以重入。

ReentrantReadWriteLock中相關(guān)的核心常量:

獲取寫鎖:

1.檢查state是不是為0。如果不是0,意味著要么有讀鎖存在 (readCount > 0),要么已經(jīng)有其他線程持有了寫鎖 (writeCount > 0)。

2.如果state不是0,但持有寫鎖的是當(dāng)前線程,就可以重入,把writeCount加1。

3.如果state是0,那么當(dāng)前線程可以獲取寫鎖,把writeCount加1,把當(dāng)前線程設(shè)置成鎖的持有者。

獲取讀鎖:

1.檢查是不是存在寫鎖 (writeCount > 0),且持有寫鎖的不是當(dāng)前線程。如果是,則獲取失敗,進(jìn)入等待隊(duì)列。

2.如果沒(méi)有寫鎖,或者寫鎖被當(dāng)前線程持有,那么當(dāng)前線程可以獲取讀鎖,通過(guò)CAS操作把readCount加1。

3.2 公平策略

ReentrantReadWriteLock跟ReentrantLock一樣,也支持公平和非公平兩種模式。

非公平鎖:new ReentrantReadWriteLock()和new ReentrantReadWriteLock(false)都是非公平鎖構(gòu)造。允許新來(lái)的線程插隊(duì)。舉個(gè)例子,當(dāng)寫鎖被釋放的時(shí)候,如果等待隊(duì)列里有讀線程和寫線程,同時(shí)又有一個(gè)新的讀線程請(qǐng)求鎖,那么這個(gè)新的讀線程可能優(yōu)先獲得鎖。從例子就能看出優(yōu)點(diǎn)肯定是吞吐量更高,但可能會(huì)導(dǎo)致寫線程饑餓。

公平鎖:new ReentrantReadWriteLock(true)。這種模式就嚴(yán)格按照線程在等待隊(duì)列里的FIFO順序來(lái)分配鎖。如果隊(duì)列頭部是寫線程在等待,那所有后來(lái)的讀鎖請(qǐng)求都必須排隊(duì)。優(yōu)點(diǎn)就是能防止饑餓,保證公平性,一般情況下性能會(huì)低于非公平鎖,畢竟要維持公平性。

3.3 鎖升降級(jí)

鎖降級(jí)指的是線程在持有寫鎖的情況下,繼續(xù)獲取讀鎖,然后釋放寫鎖。

鎖升級(jí)指的是線程在持有讀鎖的情況下,去獲取寫鎖。ReentrantReadWriteLock是不支持的。

鎖降級(jí)

考慮這樣一個(gè)場(chǎng)景,如果有一個(gè)線程更新了共享數(shù)據(jù)后,還需要讀取這些數(shù)據(jù)處理其他業(yè)務(wù),但這個(gè)時(shí)候已經(jīng)不希望再獨(dú)占資源,而是允許其他讀線程進(jìn)來(lái)。

wLock.lock(); // 獲取寫鎖
try {
    // 修改數(shù)據(jù)...
    map.put("name", "懶惰蝸牛");
    // 鎖降級(jí):在持有寫鎖的情況下獲取讀鎖
    rLock.lock();
    // 釋放寫鎖(這個(gè)時(shí)候仍然持有讀鎖)
    // 其他線程現(xiàn)在可以獲取讀鎖了
    wLock.unlock();
    // 使用讀鎖狀態(tài)讀取數(shù)據(jù),執(zhí)行后續(xù)操作...
    // 保證了在讀取期間,數(shù)據(jù)不會(huì)被其他寫線程修改
    System.out.println("讀取數(shù)據(jù): " + map.get("name"));
} finally {
    // 釋放讀鎖
    rLock.unlock(); 
}

鎖升級(jí)

為什么不支持鎖升級(jí),如果兩個(gè)線程(T1, T2)同時(shí)持有讀鎖,然后他們都嘗試升級(jí)成寫鎖。T1要等待T2釋放讀鎖,而T2也要等待T1釋放讀鎖,就這樣相互等著,最后就形成死鎖了。

所以必須先rLock.unlock(),然后再wLock.lock()。但還是要注意的是,在釋放讀鎖和獲取寫鎖的間隙,數(shù)據(jù)可能被其他線程修改,原子性被破壞了,這就需要我們自己的業(yè)務(wù)邏輯來(lái)處理。

四、小結(jié)

目前我們已經(jīng)接觸了synchronized、ReentrantLock和ReentrantReadWriteLock。

下面對(duì)這三種形式的鎖用表格進(jìn)行清晰的對(duì)比:

特性synchronizedReentrantLockReentrantReadWriteLock
鎖類型互斥鎖互斥鎖讀寫分離鎖(共享/互斥)
底層實(shí)現(xiàn)JVM指令AQSAQS
公平性非公平可選(默認(rèn)非公平)可選(默認(rèn)非公平)
可中斷不可中斷可中斷可中斷
嘗試獲取鎖不支持支持 (tryLock)支持 (tryLock)
Conditionwait/notifyCondition僅寫鎖支持Condition
適用場(chǎng)景簡(jiǎn)單同步代碼塊需高級(jí)功能的復(fù)雜同步讀多寫少的場(chǎng)景

每種形式的鎖都有各自的優(yōu)缺點(diǎn),都有自己的應(yīng)用場(chǎng)景。

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

相關(guān)文章

  • Spring中@ControllerAdvice注解的用法解析

    Spring中@ControllerAdvice注解的用法解析

    這篇文章主要介紹了Spring中@ControllerAdvice注解的用法解析,顧名思義,@ControllerAdvice就是@Controller 的增強(qiáng)版,@ControllerAdvice主要用來(lái)處理全局?jǐn)?shù)據(jù),一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用,需要的朋友可以參考下
    2023-10-10
  • Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式

    Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式

    這篇文章主要介紹了Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java使用application.property讀取文件里面的值

    Java使用application.property讀取文件里面的值

    本文通過(guò)實(shí)例代碼給大家介紹了Java使用application.property讀取文件里面的值,需要的朋友可以參考下
    2018-10-10
  • java微信紅包實(shí)現(xiàn)算法

    java微信紅包實(shí)現(xiàn)算法

    這篇文章主要為大家詳細(xì)介紹了java微信紅包實(shí)現(xiàn)算法,列出紅包的核心算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Spring?注入靜態(tài)對(duì)象使用三種方式示例

    Spring?注入靜態(tài)對(duì)象使用三種方式示例

    這篇文章主要為大家介紹了Spring注入靜態(tài)對(duì)象使用的三種方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • ElasticSearch學(xué)習(xí)之文檔API相關(guān)操作

    ElasticSearch學(xué)習(xí)之文檔API相關(guān)操作

    這篇文章主要為大家介紹了ElasticSearch學(xué)習(xí)之文檔API相關(guān)操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Java的信號(hào)量semaphore講解

    Java的信號(hào)量semaphore講解

    這篇文章主要介紹了Java的信號(hào)量semaphore講解,Semaphore底層是基于AbstractQueuedSynchronizer來(lái)實(shí)現(xiàn)的,Semaphore稱為計(jì)數(shù)信號(hào)量,它允許n個(gè)任務(wù)同時(shí)訪問(wèn)某個(gè)資源,需要的朋友可以參考下
    2023-12-12
  • Java?C++題解leetcode904水果成籃

    Java?C++題解leetcode904水果成籃

    這篇文章主要為大家介紹了Java?C++題解leetcode904水果成籃示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Springboot項(xiàng)目基于Devtools實(shí)現(xiàn)熱部署步驟詳解

    Springboot項(xiàng)目基于Devtools實(shí)現(xiàn)熱部署步驟詳解

    這篇文章主要介紹了Springboot項(xiàng)目基于Devtools實(shí)現(xiàn)熱部署,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT

    純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT

    這篇文章主要為大家詳細(xì)介紹了如何純Java環(huán)境下實(shí)現(xiàn)將在線地圖路網(wǎng)數(shù)據(jù)轉(zhuǎn)為GeoJSON與WKT,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2026-02-02

最新評(píng)論

抚顺市| 会昌县| 阿合奇县| 济宁市| 乐业县| 阿克苏市| 巫山县| 石楼县| 枣阳市| 泾阳县| 华阴市| 大港区| 赤峰市| 黄山市| 南昌市| 北安市| 宿松县| 东光县| 敖汉旗| 甘孜| 新乐市| 东港市| 临漳县| 马公市| 油尖旺区| 都兰县| 尉氏县| 科尔| 化州市| 五原县| 蕲春县| 张家口市| 称多县| 乌审旗| 长沙县| 前郭尔| 永州市| 左贡县| 沁水县| 嵊州市| 吴川市|