掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
ReentrantReadWriteLock不知道大家熟悉嗎?其實(shí)在實(shí)際的項(xiàng)目中用的比較少,反正我所在的項(xiàng)目沒(méi)有用到過(guò)。

成都創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)公司、成都網(wǎng)站制作、網(wǎng)站營(yíng)銷推廣、網(wǎng)站開(kāi)發(fā)設(shè)計(jì),對(duì)服務(wù)成都純水機(jī)等多個(gè)行業(yè)擁有豐富的網(wǎng)站建設(shè)及推廣經(jīng)驗(yàn)。成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司成立于2013年,提供專業(yè)網(wǎng)站制作報(bào)價(jià)服務(wù),我們深知市場(chǎng)的競(jìng)爭(zhēng)激烈,認(rèn)真對(duì)待每位客戶,為客戶提供賞心悅目的作品。 與客戶共同發(fā)展進(jìn)步,是我們永遠(yuǎn)的責(zé)任!
ReentrantReadWriteLock稱為讀寫鎖,它提供一個(gè)讀鎖,支持多個(gè)線程共享同一把鎖。它也提供了一把寫鎖,是獨(dú)占鎖,和其他讀鎖或者寫鎖互斥,表明只有一個(gè)線程能持有鎖資源。通過(guò)兩把鎖的協(xié)同工作,能夠最大化的提高讀寫的性能,特別是讀多寫少的場(chǎng)景,而往往大部分的場(chǎng)景都是讀多寫少的。
本文主要講解ReentrantReadWriteLock的使用和應(yīng)用場(chǎng)景。
ReentrantReadWriteLock實(shí)現(xiàn)了ReadWriteLock接口,可以獲取到讀鎖(共享鎖),寫鎖(獨(dú)占鎖)。同時(shí),通過(guò)構(gòu)造方法可以創(chuàng)建鎖本身是公平鎖還是非公鎖。
|
讀鎖 |
寫鎖 | |
|
讀鎖 |
共享 |
互斥 |
|
寫鎖 |
互斥 |
互斥 |
線程進(jìn)入讀鎖的前提條件:
進(jìn)入寫鎖的前提條件:
我們知道ReentrantLock具備可重入的能力,即同一個(gè)線程多次獲取鎖,不引起阻塞,那么ReentrantReadWriteLock關(guān)于可重入性是怎么樣的呢?
關(guān)于這個(gè)問(wèn)題需要引入兩個(gè)概念,鎖升級(jí),鎖降級(jí)。
重入時(shí)鎖升級(jí)不支持:持有讀鎖的情況下去獲取寫鎖會(huì)導(dǎo)致獲取寫鎖永久等待,需要先釋放讀,再去獲得寫
重入時(shí)鎖降級(jí)支持:持有寫鎖的情況下去獲取讀鎖,造成只有當(dāng)前線程會(huì)持有讀鎖,因?yàn)閷戞i會(huì)互斥其他的鎖
API介紹
構(gòu)造方法:
常用API:
代碼范式
r.lock();
try {
// 臨界區(qū)
} finally {
r.unlock();
}
w.lock();
try {
r.lock();// 降級(jí)為讀鎖, 釋放寫鎖, 這樣能夠讓其它線程讀取緩存
try {
// ...
} finally{
w.unlock();// 要在寫鎖釋放之前獲取讀鎖
}
} finally{
r.unlock();
}
@Test
public void readReadMode() throws InterruptedException {
ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock r = rw.readLock();
ReentrantReadWriteLock.WriteLock w = rw.writeLock();
Thread thread0 = new Thread(() -> {
r.lock();
try {
Thread.sleep(1000);
System.out.println("Thread 1 running " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
r.unlock();
}
},"t1");
Thread thread1 = new Thread(() -> {
r.lock();
try {
Thread.sleep(1000);
System.out.println("Thread 2 running " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
r.unlock();
}
},"t2");
thread0.start();
thread1.start();
thread0.join();
thread1.join();
}
運(yùn)行結(jié)果:
@Test
public void readWriteMode() throws InterruptedException {
ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock r = rw.readLock();
ReentrantReadWriteLock.WriteLock w = rw.writeLock();
Thread thread0 = new Thread(() -> {
r.lock();
try {
Thread.sleep(1000);
System.out.println("Thread 1 running " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
r.unlock();
}
},"t1");
Thread thread1 = new Thread(() -> {
w.lock();
try {
Thread.sleep(1000);
System.out.println("Thread 2 running " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
w.unlock();
}
},"t2");
thread0.start();
thread1.start();
thread0.join();
thread1.join();
}
運(yùn)行結(jié)果:
什么場(chǎng)景下讀多寫少? 想必最先想到的就是緩存把,ReentrantReadWriteLock在緩存場(chǎng)景中就是一個(gè)很典型的應(yīng)用。
緩存更新時(shí),是先清緩存還是先更新數(shù)據(jù)庫(kù)?
顯而易見(jiàn),通常情況下,先更新數(shù)據(jù)庫(kù),然后清空緩存。
public class GenericCachedDao {
// 緩存對(duì)象,這里用jvm緩存
Map cache = new HashMap<>();
// 讀寫鎖
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 讀取操作
public String getData(String key) {
// 加讀鎖,防止其他線程修改緩存
readWriteLock.readLock().lock();
try {
String value = cache.get(key);
// 如果緩存命中,返回
if(value != null) {
return value;
}
} finally {
// 釋放讀鎖
readWriteLock.readLock().unlock();
}
//如果緩存沒(méi)有命中,從數(shù)據(jù)庫(kù)中加載
readWriteLock.writeLock().lock();
try {
// 細(xì)節(jié),為防止重復(fù)查詢數(shù)據(jù)庫(kù), 再次驗(yàn)證
// 因?yàn)間et 方法上面部分是可能多個(gè)線程進(jìn)來(lái)的, 可能已經(jīng)向緩存填充了數(shù)據(jù)
String value = cache.get(key);
if(value == null) {
// 這里可以改成從數(shù)據(jù)庫(kù)查詢
value = "alvin";
cache.put(key, value);
}
return value;
} finally {
readWriteLock.writeLock().unlock();
}
}
// 更新數(shù)據(jù)
public void updateData(String key, String value) {
// 加寫鎖
readWriteLock.writeLock().lock();
try {
// 更新操作TODO
// 清空緩存
cache.remove(key);
} finally {
readWriteLock.writeLock().unlock();
}
}
}
本文講解了ReentrantReadWriteLock讀寫鎖常用的API, 以及通過(guò)幾個(gè)demo的演示,講解了讀寫鎖的使用,希望對(duì)大家有幫助。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流