怎樣用讀寫鎖快速實現一個快取?
阿新 • • 發佈:2022-03-28
1)SDK已經有管程了,不是可以解決所有的併發問題的嗎,為什麼還要有讀寫鎖?
-
不同的場景下使用不同的鎖效果是不一樣的,我們的讀寫鎖用在讀多寫少的場景下那是非常有用的。
2)讀寫鎖是我們JAVA特有的嗎?他有什麼原則?
-
讀寫鎖並不是java特有的,是通用的一個技術方案。讀寫鎖的話有三個基本原則:
-
同一時刻,允許多個執行緒去讀一個變數
-
同一時刻,只允許一個執行緒去寫變數
-
當有一個執行緒在寫變數的時候,是不能讀的
-
3)讀寫鎖在java中是怎樣實現的,怎樣使用?
-
ReadWriteLock他只是一個介面,他的實現子類是ReentrantReadWriteLock。
-
用的時候直接呼叫讀鎖或者寫鎖readLock()
4)既然說了讀寫鎖適合我們快取的場景下的使用,那讀寫鎖怎樣快速的實現一個快取呢?
-
我們定義一個類,這個類呢有k 和 v變數,代表快取的key和value。有get()讀方法,和put()寫方法。讀的時候上讀鎖,寫的時候上寫鎖。
class Cache<K,V> {
final Map<K, V> m =
new HashMap<>();
final ReadWriteLock rwl =
new ReentrantReadWriteLock();
// 讀鎖
final Lock r = rwl.readLock();
// 寫鎖
final Lock w = rwl.writeLock();
// 讀快取
V get(K key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
// 寫快取
V put(K key, V value) {
w.lock();
try { return m.put(key, v); }
finally { w.unlock(); }
}
}
5)實現快取,會存在資料初始化的問題,那麼我們快取裡面的資料該怎樣初始化呢?
-
對於資料比較少的,我們直接一次性把資料裝進快取,簡單粗暴又高效。
-
對於資料比較多的,要讀資料的時候,我們再把它存進快取裡面來。
6)上面實現初始化快取資料的的方法中,第二種程式碼是怎樣實現的?
class Cache<K,V> {
final Map<K, V> m =
new HashMap<>();
final ReadWriteLock rwl =
new ReentrantReadWriteLock();
final Lock r = rwl.readLock();
final Lock w = rwl.writeLock();
V get(K key) {
V v = null;
//讀快取
r.lock(); ①
try {
v = m.get(key); ②
} finally{
r.unlock(); ③
}
//快取中存在,返回
if(v != null) { ④
return v;
}
//快取中不存在,查詢資料庫
w.lock(); ⑤
try {
//再次驗證
//其他執行緒可能已經查詢過資料庫
v = m.get(key); ⑥
if(v == null){ ⑦
//查詢資料庫
v=省略程式碼無數
m.put(key, v);
}
} finally{
w.unlock();
}
return v;
}
}
7)為什麼要再次進行驗證?
-
因為我們的讀是併發的,不確定其它執行緒在這個期間讀過沒有,為了防止重複讀,所以要再次驗證。