ReentrantReadWriteLock讀寫鎖的使用2
來(lái)源:程序員人生 發(fā)布時(shí)間:2015-02-03 08:40:39 閱讀次數(shù):3284次
本文可作為傳智播客《張孝祥-Java多線程與并發(fā)庫(kù)高級(jí)利用》的學(xué)習(xí)筆記。
這1節(jié)我們做1個(gè)緩存系統(tǒng)。
在讀本節(jié)前
請(qǐng)先瀏覽
ReentrantReadWriteLock讀寫鎖的使用1
初版
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
System.out.println("ss "+cd.getData2("ss"));
System.out.println("ss "+cd.getData2("ss"));
System.out.println("mm "+cd.getData2("mm"));
System.out.println("mm "+cd.getData2("mm"));
}
public Object getData2(String key){
Object o=cache.get(key);
if (o==null) { //標(biāo)識(shí)1
System.out.println("第1次查 沒(méi)有"+key);
o=Math.random(); //實(shí)際上從
數(shù)據(jù)庫(kù)中取得
cache.put(key, o);
}
return o;
}
}
運(yùn)行結(jié)果:
第1次查 沒(méi)有ss
ss 0.4045014284225158
ss 0.4045014284225158
第1次查 沒(méi)有mm
mm 0.9994663041529088
mm 0.9994663041529088
似乎沒(méi)有問(wèn)題。
你覺(jué)得呢?
如果有3個(gè)線程同時(shí)第1次到了getData2()的標(biāo)識(shí)1處(所查找的key也都1樣),1檢查o為null,然后就去查
數(shù)據(jù)庫(kù)。在這類情況下,就等于3個(gè)線程查同1個(gè)key,然后都去了
數(shù)據(jù)庫(kù)。
這明顯是不公道的。
第2版 synchronized
最簡(jiǎn)單的辦法
public synchronized Object getData2(String key){
//.....
}
第3版 鎖
上1節(jié)我們提到了ReentrantLock可以替換synchronized,前者是1種更加面向?qū)ο蟮脑O(shè)計(jì)。那我們?cè)囋嚒?br>
private Lock l=new ReentrantLock(); //l作為CacheDemo的成員變量
public Object getData3(String key) {
l.lock();
Object o=null;
try {
o = cache.get(key);
if (o == null) { // 標(biāo)識(shí)1
System.out.println("第1次查 沒(méi)有" + key);
o = Math.random(); // 實(shí)際上從
數(shù)據(jù)庫(kù)中取得
cache.put(key, o);
}
} finally {
l.unlock();
}
return o;
}
第3版可以嗎?
可以個(gè)p。
為何,自己想。
我們得使用ReentrantReadWriteLock,在同1個(gè)方法中既有讀鎖也有寫鎖。
首先我又1個(gè)問(wèn)題:
對(duì)同1個(gè)線程,可以在加了讀鎖后,沒(méi)有解開(kāi)讀鎖前,再加寫鎖嗎?
換句話說(shuō),下面的代碼會(huì)停嗎?
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
cd.getData2();
}
public void getData2(){
rwl.readLock().lock();
System.out.println(1);
rwl.writeLock().lock();
System.out.println(2);
rwl.readLock().unlock();
System.out.println(3);
rwl.writeLock().unlock();
System.out.println(4);
}
}
親身試1下,控制臺(tái)輸出1后,程序就不動(dòng)了。
說(shuō)明加寫鎖前,讀鎖得先解開(kāi)。
第4版
即有讀鎖,又有寫鎖
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
System.out.println("ss "+cd.getData4("ss"));
System.out.println("ss "+cd.getData4("ss"));
System.out.println("mm "+cd.getData4("mm"));
System.out.println("mm "+cd.getData4("mm"));
}
public Object getData4(String key){
rwl.readLock().lock();
Object o=null;
try {
o=cache.get(key);
if (o==null) {
System.out.println("第1次查 沒(méi)有"+key);
rwl.readLock().unlock(); //標(biāo)識(shí)4
rwl.writeLock().lock();
try {
if(value==null){ //標(biāo)識(shí)0
value = "aaaa";//實(shí)際調(diào)用 queryDB();
cache.put(key,value);
}
} finally {
rwl.writeLock().unlock();
}
rwl.readLock().lock(); //標(biāo)識(shí)1
}
}finally {
rwl.readLock().unlock(); //標(biāo)注2
}
return o;
}
結(jié)果
第1次查 沒(méi)有ss
ss 0.5989899889645358
ss 0.5989899889645358
第1次查 沒(méi)有mm
mm 0.8534424949014686
mm 0.8534424949014686
這個(gè)還有3個(gè)問(wèn)題
1為何在標(biāo)識(shí)2處還得解鎖。
這個(gè)答案在上1節(jié)已提過(guò)。
2為何在標(biāo)識(shí)1出還得加鎖?
如果標(biāo)識(shí)1處不加鎖,且程序1直正常履行,那末到標(biāo)識(shí)2處,它解誰(shuí)的鎖?
3為何標(biāo)識(shí)0出還要檢查1次?
如果同時(shí)又3個(gè)線程運(yùn)行到標(biāo)識(shí)4(查找相同的key),其中1個(gè)取得寫鎖,寫入數(shù)據(jù)后,釋放了寫鎖。此時(shí)另外兩個(gè)線程還需要去
數(shù)據(jù)庫(kù)跑1趟么?
另外把標(biāo)識(shí)1處的加讀鎖,放到finally的前面稱之為降級(jí)鎖。對(duì)降級(jí)鎖,我目前也不太清楚。
官方文檔中給了1個(gè)例子就是關(guān)于降級(jí)鎖,以下
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
感謝glt
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)