成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

通俗易懂讀寫鎖ReentrantReadWriteLock的使用

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

?概述

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

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

本文主要講解ReentrantReadWriteLock的使用和應用場景。

ReentrantReadWriteLock介紹

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

讀寫鎖機制:


讀鎖

寫鎖

讀鎖

共享

互斥

寫鎖

互斥

互斥

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

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

進入寫鎖的前提條件:

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

鎖升級、降級機制:

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

關于這個問題需要引入兩個概念,鎖升級,鎖降級。

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

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

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

API介紹

構造方法:

  • public ReentrantReadWriteLock():默認構造方法,非公平鎖
  • 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 {
// 臨界區
} finally {
r.unlock();
}
  • 鎖降級
w.lock();
try {
r.lock();// 降級為讀鎖, 釋放寫鎖, 這樣能夠讓其它線程讀取緩存
try {
// ...
} finally{
w.unlock();// 要在寫鎖釋放之前獲取讀鎖
}
} finally{
r.unlock();
}

實戰案例

驗證讀讀共享模式

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

運行結果:

圖片

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

驗證讀寫互斥模式

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

運行結果:

圖片

  • 兩個線程間隔1秒,互斥執行

真實緩存例子

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

圖片

緩存更新時,是先清緩存還是先更新數據庫?

  • 先清緩存:可能造成剛清理緩存還沒有更新數據庫,高并發下,其他線程直接查詢了數據庫過期數據到緩存中,這種情況非常嚴重,直接導致后續所有的請求緩存和數據庫不一致。
  • 先更新據庫:可能造成剛更新數據庫,還沒清空緩存就有線程從緩存拿到了舊數據,這種情況概率比較小,影響范圍有限,只對這一次的查詢結果有問題。

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

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

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

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

// 清空緩存
cache.remove(key);
} finally {
readWriteLock.writeLock().unlock();
}
}
}
  • getData方法是讀取操作,先加讀鎖,從緩存讀取,如果沒有命中,加寫鎖,此時其他線程就不能讀取了,等寫入成功后,釋放讀鎖。
  • updateData方法是寫操作,更新時加寫鎖,其他線程此時無法讀取,然后清空緩存中的舊數據。

總結

本文講解了ReentrantReadWriteLock讀寫鎖常用的API, 以及通過幾個demo的演示,講解了讀寫鎖的使用,希望對大家有幫助。

責任編輯:武曉燕 來源: JAVA旭陽
相關推薦

2011-10-26 19:57:33

2019-06-19 08:30:47

網絡協議IPTCP

2020-06-08 10:50:58

前端TypeScript代碼

2021-05-26 16:12:20

區塊鏈加密貨幣比特幣

2022-06-28 07:31:11

哨兵模式redis

2022-09-23 08:32:53

微服務架構服務

2021-11-04 08:16:50

MySQL SQL 語句數據庫

2022-07-06 08:17:50

C 語言函數選型

2019-05-20 07:37:00

TCPIP網絡協議

2018-01-17 22:36:46

區塊鏈數字貨幣比特幣

2023-01-04 13:43:24

讀寫鎖AQS共享模式

2021-05-30 19:02:59

變量對象上下文

2021-05-25 09:50:01

GitLinux命令

2018-03-11 15:11:38

物聯網數據物聯網數據

2018-03-05 08:35:12

物聯網互聯網網絡技術

2021-05-13 13:20:00

Git命令Linux

2018-03-29 06:40:26

物聯網

2018-03-11 14:57:07

物聯網組網無線通信

2023-08-03 16:02:24

Objectwaitnotify

2019-08-27 09:20:35

微服務架構組件
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成年人视频免费在线观看 | 国产一区二区久久 | 九九热视频这里只有精品 | 日韩在线中文字幕 | 欧美色综合一区二区三区 | 九九热精品视频 | 视频一二区 | 国产精品久久久久久中文字 | 在线免费观看欧美 | 日韩最新网站 | 高清视频一区二区三区 | 九九精品影院 | 欧美亚洲国产日韩 | 99色综合| 在线视频亚洲 | 日韩欧美1区2区 | 国产精品美女久久久久aⅴ国产馆 | 99re6在线视频 | 97精品超碰一区二区三区 | av一区二区三区在线观看 | 区一区二区三在线观看 | 丝袜美腿一区二区三区动态图 | 在线观看精品 | 国产成人精品999在线观看 | 久久国内精品 | av男人的天堂在线 | 欧美日韩国产一区二区三区 | 亚洲综合视频 | 99久久精品免费看国产免费软件 | 久久成人精品视频 | 国产福利在线免费观看 | 日韩精品一区二 | 成人h动漫亚洲一区二区 | 欧美一区二区三区的 | 日韩不卡三区 | 日韩免费一二三区 | 日韩视频精品在线 | 一级黄色网页 | 久久久久久久久久久福利观看 | 嫩草视频网站 | 天天天天操 |