所謂讀寫鎖,是對訪問資源共享鎖和排斥鎖,一般的重入性語義爲 如果對資源加了寫鎖,其他線程無法再獲得寫鎖與讀鎖,但是持有寫鎖的線程,可以對資源加讀鎖(鎖降級);如果一個線程對資源加了讀鎖,其他線程可以繼續加讀鎖。
下面的代碼展示瞭如何利用重入來執行升級緩存後的鎖降級(爲簡單起見,省略了異常處理及部分代碼):
class CachedData {
Object data;
//保證狀態可見性
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// 在獲取寫鎖前必須釋放讀鎖
rwl.readLock().unlock();
rwl.writeLock().lock();
//再次檢查其他線程是否已經搶到
if (!cacheValid) {
//獲取數據
data = ...
cacheValid = true;
}
// 在釋放寫鎖之前通過獲取讀鎖來降級
rwl.readLock().lock();
//釋放寫鎖,保持讀鎖
rwl.writeLock().unlock();
}
use(data);
rwl.readLock().unlock();
}
}
ReentrantReadWriteLock並沒有繼承ReentrantLock,也並沒有實現Lock接口,而是實現了ReadWriteLock接口,該接口提供readLock()方法獲取讀鎖,writeLock()獲取寫鎖。
在使用某些種類的 Collection 時,可以使用 ReentrantReadWriteLock 來提高併發性。通常,在預期 collection 很大,讀取者線程訪問它的次數多於寫入者線程,並且 entail 操作的開銷高於同步開銷時,這很值得一試。例如,以下是一個使用 TreeMap 的類,預期它很大,並且能被同時訪問。
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}