1. 程式人生 > 其它 >怎樣用讀寫鎖快速實現一個快取?

怎樣用讀寫鎖快速實現一個快取?

1)SDK已經有管程了,不是可以解決所有的併發問題的嗎,為什麼還要有讀寫鎖?

  • 不同的場景下使用不同的鎖效果是不一樣的,我們的讀寫鎖用在讀多寫少的場景下那是非常有用的。

2)讀寫鎖是我們JAVA特有的嗎?他有什麼原則?

  • 讀寫鎖並不是java特有的,是通用的一個技術方案。讀寫鎖的話有三個基本原則:

    • 同一時刻,允許多個執行緒去讀一個變數

    • 同一時刻,只允許一個執行緒去寫變數

    • 當有一個執行緒在寫變數的時候,是不能讀的

3)讀寫鎖在java中是怎樣實現的,怎樣使用?

  • ReadWriteLock他只是一個介面,他的實現子類是ReentrantReadWriteLock。

  • 用的時候直接呼叫讀鎖或者寫鎖readLock()

4)既然說了讀寫鎖適合我們快取的場景下的使用,那讀寫鎖怎樣快速的實現一個快取呢?

  • 我們定義一個類,這個類呢有k 和 v變數,代表快取的key和value。有get()讀方法,和put()寫方法。讀的時候上讀鎖,寫的時候上寫鎖。

     class Cache<K,V> {
       final Map<K, V> m =
         new HashMap<>();
       final ReadWriteLock rwl =
         new ReentrantReadWriteLock();
       // 讀鎖
       final Lock r = rwl.readLock();
       // 寫鎖
       final Lock w = rwl.writeLock();
       // 讀快取
       V get(K key) {
         r.lock();
         try { return m.get(key); }
         finally { r.unlock(); }
      }
       // 寫快取
       V put(K key, V value) {
         w.lock();
         try { return m.put(key, v); }
         finally { w.unlock(); }
      }
     }

5)實現快取,會存在資料初始化的問題,那麼我們快取裡面的資料該怎樣初始化呢?

  • 對於資料比較少的,我們直接一次性把資料裝進快取,簡單粗暴又高效。

  • 對於資料比較多的,要讀資料的時候,我們再把它存進快取裡面來。

6)上面實現初始化快取資料的的方法中,第二種程式碼是怎樣實現的?

 
 class Cache<K,V> {
   final Map<K, V> m =
     new HashMap<>();
   final ReadWriteLock rwl =
     new ReentrantReadWriteLock();
   final Lock r = rwl.readLock();
   final Lock w = rwl.writeLock();
 
   V get(K key) {
     V v = null;
     //讀快取
     r.lock();        
     try {
       v = m.get(key);
    } finally{
       r.unlock();    
    }
     //快取中存在,返回
     if(v != null) {  
       return v;
    }  
     //快取中不存在,查詢資料庫
     w.lock();        
     try {
       //再次驗證
       //其他執行緒可能已經查詢過資料庫
       v = m.get(key);
       if(v == null){  
         //查詢資料庫
         v=省略程式碼無數
         m.put(key, v);
      }
    } finally{
       w.unlock();
    }
     return v;
  }
 }

7)為什麼要再次進行驗證?

  • 因為我們的讀是併發的,不確定其它執行緒在這個期間讀過沒有,為了防止重複讀,所以要再次驗證。

注意:我們的讀寫鎖是可以升級和降級的。但是java中是不可以升級的,升級的話會造成死鎖的現象發生。