Java併發 -- ReadWriteLock
- 理論上,利用管程 和訊號量 可以解決所有併發問題,但JUC提供了很多工具類, 細分場景優化效能,提升易用性
- 針對讀多寫少 的併發場景,JUC提供了讀寫鎖 ,即ReadWriteLock
讀寫鎖
- 讀寫鎖是一種廣泛使用的通用技術 ,並非Java所特有
-
所有讀寫鎖都遵守3條基本原則
- 允許多個執行緒同時讀 共享變數 – 與互斥鎖的重要區別
- 只允許一個執行緒寫 共享變數
- 如果一個寫執行緒正常執行寫操作,此時禁止讀執行緒讀取共享變數
快取
public class Cache<K, V> { private final Map<K, V> map = new HashMap<>(); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 讀鎖 private final Lock readLock = readWriteLock.readLock(); // 寫鎖 private final Lock writeLock = readWriteLock.writeLock(); // 讀快取 -- 懶載入 public V get(K key) { V v = null; // 讀快取 readLock.lock(); // 1 try { v = map.get(key); // 2 } finally { readLock.unlock(); // 3 } // 快取中存在,直接返回 if (v != null) { // 4 return v; } // 快取中不存在,查詢資料庫 writeLock.lock(); // 5 try { // 再次驗證,其他執行緒可能已經查詢過資料庫了 // 避免在高併發場景下重複查詢資料庫的問題 v = map.get(key); // 6 if (v == null) { // 7 // 查詢資料庫 v = loadFromDb(key); map.put(key, v); } } finally { writeLock.unlock(); } return v; } private V loadFromDb(K key) { return null; } // 寫快取 public V put(K key, V value) { writeLock.lock(); try { return map.put(key, value); } finally { writeLock.unlock(); } } }
鎖升級
ReadWriteLock不支援鎖升級,readLock還沒有釋放,無法獲取writeLock,導致 執行緒阻塞
readLock.lock(); try { V v = map.get(key); if (v == null) { writeLock.lock(); try { map.put(key, loadFromDb(key)); } finally { writeLock.unlock(); } } } finally { readLock.unlock(); }
鎖降級
readLock.lock(); if (!cacheValid) { // 因為不允許讀鎖升級,先釋放讀鎖 readLock.unlock(); writeLock.lock(); try { if (!cacheValid) { cacheValid = true; } // 釋放寫鎖前,降級為讀鎖,這是允許的 readLock.lock(); // 1 } finally { writeLock.unlock(); } } // 此時仍然持有讀鎖 try { // 使用資料 } finally { readLock.unlock(); }
轉載請註明出處:http://zhongmingmao.me/2019/05/09/java-concurrent-readwrite-lock/
訪問原文「Java併發 -- ReadWriteLock 」獲取最佳閱讀體驗並參與討論