跑起一個程式,并不難;難的是,能讓程式跑多遠!—— 一顆剽悍的種子

JUC并發系列
JUC并發系列(一):什么?聽說你搞混了并發和并行
JUC并發系列(二):詳解Condition實作精準通知喚醒
JUC并發系列(三):面試問并發,一問鎖就懵(怒肝一篇透徹理解鎖,面試不慌)
JUC并發系列(四):【面試常問】多種方法解決ArrayList非執行緒安全,詳解CopyOnWriteArrayList
JUC并發系列(五):CopyOnWriteArraySet解決HashSet非執行緒安全
JUC并發系列(六):ConcurrentHashMap解決HashMap非執行緒安全
JUC并發系列(七):觸及Callable
JUC并發系列(八):并發編程常用輔助類CountDownLatch與CyclicBarrier
JUC并發系列(九):并發編程常用輔助類Semaphore
一、什么是 ReadWriteLock讀寫鎖?
準確來說 ReadWriteLock讀寫鎖,應該是 讀/寫鎖,正如ReadWriteLock 讀/寫鎖也是分開來的,它管理著兩個為一組的鎖,一個是只讀鎖,一個是寫鎖,
通過字面意思告訴你 ReadWriteLock讀/寫鎖 其實你是很難理解的,因為只有通過案例,只有通過一行行代碼去驗證,你才能在深深的理解后有自己的總結,
有自己總結過后的知識才是你的,因為那是知識的結晶,
(技術之余總會扯這些有的沒的,只想告訴你不要光看我的博文,學習這種活,也只有你理解透后,用自己所能理解的語言重塑,才是你自己的,)

我們接下來用兩個小案例,首先是一個沒有使用鎖和使用ReadWriteLock讀寫鎖后的形式對比來學習,
二、手敲代碼示例——并發下的快取
我們通過一個快取的小案例來,在沒有使用鎖的情況下,實作存盤和讀取的功能,并通過在多個執行緒的并發下,
public class Demo{
public static void main(String[] args) {
Cache cache = new Cache();
//存盤
for (int i = 1; i <= 3; i++) {
final Integer index = i;
new Thread(()->{
cache.put(index+"",index);
},String.valueOf(i)).start();
}
//讀取
for (int i = 1; i <= 3; i++) {
final Integer index = i;
new Thread(()->{
cache.get(index+"");
},String.valueOf(i)).start();
}
}
}
class Cache{
private Map<String,Integer> map = new HashMap<>();
//存盤
public void put(String key,Integer value){
System.out.println("執行緒"+Thread.currentThread().getName() + "===存盤" + value);
map.put(key,value);
System.out.println("執行緒"+Thread.currentThread().getName() + "===已存盤");
}
//讀取
public void get(String key){
System.out.println("執行緒"+Thread.currentThread().getName() + "===讀取");
map.get(key);
System.out.println("執行緒"+Thread.currentThread().getName() + "===已讀取");
}
}
三、運行結果

四、分析結果
觀察上面的代碼所運行的結果會發現兩點:
- 當執行緒1在存盤時被其他執行緒插入,

- 每次執行的結果都跟上一次不一致,

五、關鍵代碼剖析:讀/寫鎖里的獨占鎖與共享鎖
如果你理解了上面為什么ReadWriteLock讀/寫鎖是分成兩個為一組的讀/寫,其實你在實際運用中也就能更深刻透徹,
我們快取 put 是存盤就是寫入,可以用 writeLock寫鎖,
而讀取 get 可以用讀鎖,
可以看到不會像synchronized直接鎖或lock,它們更加細致,也更具有針對性的分而治之,
這里的寫鎖可以說是共享鎖,獨占鎖又稱排他鎖,
共享鎖(寫鎖):一次只能一個執行緒持有,
readWriteLock.writeLock().lock();
共享鎖(讀鎖):多個執行緒可以同時持有,
readWriteLock.readLock().lock();
讀/寫鎖使用后都需要分別關閉,跟Lock最后也需要手動關閉是一樣一樣的,
readWriteLock.writeLock().unlock();
readWriteLock.readLock().unlock();
六、使用ReadWriteLock讀/寫鎖解決快取并發問題
public class Demo{
public static void main(String[] args) {
Cache cache = new Cache();
//存盤
for (int i = 1; i <= 3; i++) {
final Integer index = i;
new Thread(()->{
cache.put(index+"",index);
},String.valueOf(i)).start();
}
//讀取
for (int i = 1; i <= 3; i++) {
final Integer index = i;
new Thread(()->{
cache.get(index+"");
},String.valueOf(i)).start();
}
}
}
class Cache{
private Map<String,Integer> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存盤
public void put(String key,Integer value){
readWriteLock.writeLock().lock();
try {
System.out.println("執行緒"+Thread.currentThread().getName() + "===存盤" + value);
map.put(key,value);
System.out.println("執行緒"+Thread.currentThread().getName() + "===已存盤");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
//讀取
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println("執行緒"+Thread.currentThread().getName() + "===讀取");
map.get(key);
System.out.println("執行緒"+Thread.currentThread().getName() + "===已讀取");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
七、運行結果(漂亮)
可以看到和上面沒有加鎖的運行結果,加了ReadWriteLock讀寫鎖 后很直觀的結果,在多執行緒并發下存盤的時候會有秩序的執行,

八、應用場景
從 ReadWriteLock讀/寫鎖 的不僅概念或者案例的對比和驗證中可以很直觀的發現其特性,
使用 ReadWriteLock讀/寫鎖 時, 適用少數執行緒修改操作,但可以有大量執行緒讀取,
所以可以運用的場景有很多很多,例如,CSDN里的每一篇博文底下都有 評論與回復,可以看做是 寫入操作,它是不頻繁;但是,讀取操作,也就是 瀏覽,是非常頻繁的,這種型別的應用場景可以使用ReadWriteLock讀/寫鎖
九、最后
最后的最后,為了更好的閱讀體驗,我把想說的話都放在了下面,嘿嘿,
我是一顆剽悍的種子 把我會的,認真的分享 是我寫博客一直不變的信條,
如果你能看到這篇博文,說明咱們還是很有緣的;希望能帶給你一些許幫助,創作的不易, 把我文章的知識帶走,你的三連留下,點贊,評論,關注,是我最大的動力,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/161902.html
標籤:其他
上一篇:城市排水工程規劃
下一篇:c++ 實作遠程CMD
