跑起一個程式,並不難;難的是,能讓程式跑多遠!—— 一顆剽悍的種子
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讀寫鎖後的形式對比來學習。
我們通過一個快取的小案例來,在沒有使用鎖的情況下,實現儲存和讀取的功能,並通過在多個執行緒的並行下。
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() + "===已讀取");
}
}
觀察上面的程式碼所執行的結果會發現兩點:
如果你理解了上面為什麼ReadWriteLock讀/寫鎖是分成兩個為一組的讀/寫,其實你在實際運用中也就能更深刻透徹。
我們快取 put 是儲存就是寫入,可以用 writeLock寫鎖。
而讀取 get 可以用讀鎖。
可以看到不會像synchronized直接鎖或lock,它們更加細緻,也更具有針對性的分而治之。
這裡的寫鎖可以說是共用鎖,獨佔鎖又稱排他鎖。
共用鎖(寫鎖):一次只能一個執行緒持有。
readWriteLock.writeLock().lock();
共用鎖(讀鎖):多個執行緒可以同時持有。
readWriteLock.readLock().lock();
讀/寫鎖使用後都需要分別關閉,跟Lock最後也需要手動關閉是一樣一樣的。
readWriteLock.writeLock().unlock();
readWriteLock.readLock().unlock();
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讀/寫鎖
最後的最後,為了更好的閱讀體驗,我把想說的話都放在了下面,嘿嘿。
我是一顆剽悍的種子 把我會的,認真的分享 是我寫部落格一直不變的信條。
如果你能看到這篇博文,說明咱們還是很有緣的;希望能帶給你一些許幫助,創作的不易, 把我文章的知識帶走,你的三連留下,點贊,評論,關注,是我最大的動力。