下面這個例子非常實用,我是 javadoc 的搬運工:
// 這是一個關于緩存操作的故事
classCachedData{
Object data;
volatileboolean cacheValid;
// 讀寫鎖執行個體
final ReentrantReadWriteLock rwl =newReentrantReadWriteLock();
voidprocessCachedData(){
// 擷取讀鎖
rwl.readLock().lock();
if(!cacheValid) {// 如果緩存過期了,或者為 null
// 釋放掉讀鎖,然後擷取寫鎖 (後面會看到,沒釋放掉讀鎖就擷取寫鎖,會發生死鎖情況)
rwl.readLock().unlock();
rwl.writeLock().lock();
try{
if(!cacheValid) {// 重新判斷,因為在等待寫鎖的過程中,可能前面有其他寫線程執行過了
data = ...
cacheValid =true;
}
// 擷取讀鎖 (持有寫鎖的情況下,是允許擷取讀鎖的,稱為 “鎖降級”,反之不行。)
rwl.readLock().lock();
}finally{
// 釋放寫鎖,此時還剩一個讀鎖
rwl.writeLock().unlock();// Unlock write, still hold read
}
}
try{
use(data);
}finally{
// 釋放讀鎖
rwl.readLock().unlock();
}
}
}
ReentrantReadWriteLock 分為讀鎖和寫鎖兩個執行個體,讀鎖是共享鎖,可被多個線程同時使用,寫鎖是獨占鎖。持有寫鎖的線程可以繼續擷取讀鎖,反之不行。
這一節比較重要,我們要先看清楚 ReentrantReadWriteLock 的大架構,然後再到源碼細節。
首先,我們來看下 ReentrantReadWriteLock 的結構,它有好些嵌套類:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuUjM0UzM4QjNyQDM3ATMwIjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
大家先仔細看看這張圖中的資訊。然後我們把 ReadLock 和 WriteLock 的代碼提出來一起看,清晰一些:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuUjM0UzM4QjNyQDM3ATMwIjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
很清楚了,ReadLock 和 WriteLock 中的方法都是通過 Sync 這個類來實作的。Sync 是 AQS 的子類,然後再派生了公平模式和不公平模式。
從它們調用的 Sync 方法,我們可以看到:ReadLock 使用了共享模式,WriteLock 使用了獨占模式。
等等,同一個 AQS 執行個體怎麼可以同時使用共享模式和獨占模式???
這裡給大家回顧下 AQS,我們橫向對比下 AQS 的共享模式和獨占模式:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuUjM0UzM4QjNyQDM3ATMwIjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
AQS 的精髓在于内部的屬性state:
對于獨占模式來說,通常就是 0 代表可擷取鎖,1 代表鎖被别人擷取了,重入例外
而共享模式下,每個線程都可以對 state 進行加減操作
也就是說,獨占模式和共享模式對于 state 的操作完全不一樣,那讀寫鎖 ReentrantReadWriteLock 中是怎麼使用 state 的呢?答案是将 state 這個 32 位的 int 值分為高 16 位和低 16位,分别用于共享模式和獨占模式。
作者:慕容千語
連結:https://www.jianshu.com/p/27ac83f340c4