【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