1. 程式人生 > >【Java多執行緒】ReentrantReadWriteLock

【Java多執行緒】ReentrantReadWriteLock

概述

  ReentrantReadWriteLock是Lock的另一種實現方式,ReentrantLock是一個排他鎖,同一時間只允許一個執行緒訪問,而ReentrantReadWriteLock允許多個讀執行緒同時訪問,但不允許寫執行緒和讀執行緒、寫執行緒和寫執行緒同時訪問。相對於排他鎖,提高了併發性。在實際應用中,大部分情況下對共享資料(如快取)的訪問都是讀操作遠多於寫操作,這時ReentrantReadWriteLock能夠提供比排他鎖更好的併發性和吞吐量。

  所謂讀寫鎖,是對訪問資源共享鎖和排斥鎖,一般的重入性語義為 如果對資源加了寫鎖,其他執行緒無法再獲得寫鎖與讀鎖,但是持有寫鎖的執行緒,可以對資源加讀鎖(鎖降級);如果一個執行緒對資源加了讀鎖,其他執行緒可以繼續加讀鎖。

  執行緒進入讀鎖的前提條件:

    沒有其他執行緒的寫鎖,

    沒有寫請求或者有寫請求,但呼叫執行緒和持有鎖的執行緒是同一個。

  執行緒進入寫鎖的前提條件:

    沒有其他執行緒的讀鎖

    沒有其他執行緒的寫鎖

使用

  示例一:利用重入來執行升級快取後的鎖降級

複製程式碼
 1 class CachedData {
 2     Object data;
 3     volatile boolean cacheValid;    //快取是否有效
 4     ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
 5 
 6     void processCachedData() {
 7         rwl.readLock().lock();    //獲取讀鎖
 8         //如果快取無效,更新cache;否則直接使用data
 9         if (!cacheValid) {
10             // Must release read lock before acquiring write lock
11             //獲取寫鎖前須釋放讀鎖
12             rwl.readLock().unlock();
13             rwl.writeLock().lock();    
14             // Recheck state because another thread might have acquired
15             //   write lock and changed state before we did.
16             if (!cacheValid) {
17                 data = ...
18                 cacheValid = true;
19             }
20             // Downgrade by acquiring read lock before releasing write lock
21             //鎖降級,在釋放寫鎖前獲取讀鎖
22             rwl.readLock().lock();
23             rwl.writeLock().unlock(); // Unlock write, still hold read
24         }
25 
26         use(data);
27         rwl.readLock().unlock();    //釋放讀鎖
28     }
29 }
複製程式碼

  示例二:使用 ReentrantReadWriteLock 來提高 Collection 的併發性

    通常在 collection 資料很多,讀執行緒訪問多於寫執行緒並且 entail 操作的開銷高於同步開銷時嘗試這麼做。

複製程式碼
 1 class RWDictionary {
 2     private final Map<String, Data> m = new TreeMap<String, Data>();
 3     private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
 4     private final Lock r = rwl.readLock();    //讀鎖
 5     private final Lock w = rwl.writeLock();    //寫鎖
 6 
 7     public Data get(String key) {
 8         r.lock();
 9         try { return m.get(key); }
10         finally { r.unlock(); }
11     }
12     public String[] allKeys() {
13         r.lock();
14         try { return m.keySet().toArray(); }
15         finally { r.unlock(); }
16     }
17     public Data put(String key, Data value) {
18         w.lock();
19         try { return m.put(key, value); }
20         finally { w.unlock(); }
21     }
22     public void clear() {
23         w.lock();
24         try { m.clear(); }
25         finally { w.unlock(); }
26     }
27 }
複製程式碼

 

實現原理

  ReentrantReadWriteLock 也是基於AQS實現的,它的自定義同步器(繼承AQS)需要在同步狀態(一個整型變數state)上維護多個讀執行緒和一個寫執行緒的狀態,使得該狀態的設計成為讀寫鎖實現的關鍵。如果在一個整型變數上維護多種狀態,就一定需要“按位切割使用”這個變數,讀寫鎖將變數切分成了兩個部分,高16位表示讀,低16位表示寫。

轉自:https://www.cnblogs.com/zaizhoumo/p/7782941.html,https://blog.csdn.net/prestigeding/article/details/53286756