天天看點

java讀寫鎖死鎖例子_Java 讀寫鎖 ReentrantReadWriteLock 源碼分析

下面這個例子非常實用,我是 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 的結構,它有好些嵌套類:

java讀寫鎖死鎖例子_Java 讀寫鎖 ReentrantReadWriteLock 源碼分析

大家先仔細看看這張圖中的資訊。然後我們把 ReadLock 和 WriteLock 的代碼提出來一起看,清晰一些:

java讀寫鎖死鎖例子_Java 讀寫鎖 ReentrantReadWriteLock 源碼分析

很清楚了,ReadLock 和 WriteLock 中的方法都是通過 Sync 這個類來實作的。Sync 是 AQS 的子類,然後再派生了公平模式和不公平模式。

從它們調用的 Sync 方法,我們可以看到:ReadLock 使用了共享模式,WriteLock 使用了獨占模式。

等等,同一個 AQS 執行個體怎麼可以同時使用共享模式和獨占模式???

這裡給大家回顧下 AQS,我們橫向對比下 AQS 的共享模式和獨占模式:

java讀寫鎖死鎖例子_Java 讀寫鎖 ReentrantReadWriteLock 源碼分析

AQS 的精髓在于内部的屬性state:

對于獨占模式來說,通常就是 0 代表可擷取鎖,1 代表鎖被别人擷取了,重入例外

而共享模式下,每個線程都可以對 state 進行加減操作

也就是說,獨占模式和共享模式對于 state 的操作完全不一樣,那讀寫鎖 ReentrantReadWriteLock 中是怎麼使用 state 的呢?答案是将 state 這個 32 位的 int 值分為高 16 位和低 16位,分别用于共享模式和獨占模式。

作者:慕容千語

連結:https://www.jianshu.com/p/27ac83f340c4