搞定ReentrantReadWriteLock 幾道小小數學題就夠了
阿新 • • 發佈:2020-06-24
| **好看請贊,養成習慣**
> - 你有一個思想,我有一個思想,我們交換後,一個人就有兩個思想
>
> - If you can NOT explain it simply, you do NOT understand it well enough
現陸續將Demo程式碼和技術文章整理在一起 [Github實踐精選](https://github.com/FraserYu/learnings) ,方便大家閱讀檢視,本文同樣收錄在此,覺得不錯,還請Star
![](https://img2020.cnblogs.com/other/1583165/202006/1583165-20200624084554199-997994831.png)
## 前言
- 文章 [Java AQS佇列同步器以及ReentrantLock的應用](https://dayarch.top/p/java-aqs-and-reentrantlock.html) 介紹了AQS**獨佔式獲取同步狀態**的實現,並以 ReentrantLock 為例說明其是如何自定義同步器實現互斥鎖的
- 文章 [Java AQS共享式獲取同步狀態及Semaphore的應用分析](https://dayarch.top/p/java-aqs-acquireshared-and-semaphore.html) 介紹 AQS **共享式獲取同步狀態**的實現,並說明了 Semaphore 是如何自定義同步器實現簡單限流作用的
有了以上兩篇文章的鋪墊,來理解本文要介紹的既有獨佔式,又有共享式獲取同步狀態的 `ReadWriteLock`,就非常輕鬆了
![](https://img2020.cnblogs.com/other/1583165/202006/1583165-20200624084554650-1070257189.png)
## ReadWriteLock
`ReadWriteLock` 直譯過來為【讀寫鎖】。現實中,讀多寫少的業務場景是非常普遍的,比如應用快取
> 一個執行緒將資料寫入快取,其他執行緒可以直接讀取快取中的資料,提高資料查詢效率
之前提到的互斥鎖都是排他鎖,也就是說同一時刻只允許一個執行緒進行訪問,當面對可共享讀的業務場景,互斥鎖顯然是比較低效的一種處理方式。為了提高效率,讀寫鎖模型就誕生了
效率提升是一方面,但併發程式設計更重要的是在保證準確性的前提下提高效率
> 一個寫執行緒改變了快取中的值,其他讀執行緒一定是可以 **“感知”** 到的,否則可能導致查詢到的值不準確
所以關於讀寫鎖模型就了下面這 3 條規定:
1. 允許多個執行緒同時讀共享變數
2. 只允許一個執行緒寫共享變數
3. 如果寫執行緒正在執行寫操作,此時則禁止其他讀執行緒讀共享變數
`ReadWriteLock` 是一個介面,其內部只有兩個方法:
```java
public interface ReadWriteLock {
// 返回用於讀的鎖
Lock readLock();
// 返回用於寫的鎖
Lock writeLock();
}
```
所以要了解整個讀/寫鎖的整個應用過程,需要從它的實現類 `ReentrantReadWriteLock` 說起
### ReentrantReadWriteLock 類結構
直接對比ReentrantReadWriteLock 與 ReentrantLock的類結構
![](https://img2020.cnblogs.com/other/1583165/202006/1583165-20200624084555158-1388166534.png)
他們又很相似吧,根據類名稱以及類結構,按照咱們前序文章的分析,你也就能看出 ReentrantReadWriteLock 的基本特性:
![](https://img2020.cnblogs.com/other/1583165/202006/1583165-20200624084555709-4292919.png)
其中黃顏色標記的的 **鎖降級** 是看不出來的, 這裡先有個印象,下面會單獨說明
另外,不知道你是否還記得,[Java AQS佇列同步器以及ReentrantLock的應用](https://dayarch.top/p/java-aqs-and-reentrantlock.html) 說過,Lock 和 AQS 同步器是一種組合形式的存在,既然這裡是讀/寫兩種鎖,他們的組合模式也就分成了兩種:
1. 讀鎖與自定義同步器的聚合
2. 寫鎖與自定義同步器的聚合
```java
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
```
![](https://img2020.cnblogs.com/other/1583165/202006/1583165-20200624084556162-374219872.png)
這裡只是提醒大家,模式沒有變,不要被讀/寫兩種鎖迷惑
#### 基本示例
說了這麼多,如果你忘了前序知識,整體理解感覺應該是有斷檔的,所以先來看個示例(模擬使用快取)讓大家對 ReentrantReadWriteLock 有個直觀的使用印象
```java
public class ReentrantReadWriteLockCache {
// 定義一個非執行緒安全的 HashMap 用於快取物件
st