2011-11-18 45 views
1

在我的應用程序中,我需要確保對於表示數據庫中的數據行的實體 我最多隻有一個代表它的java對象。WeakMultiton:確保對於特定數據庫行只有一個對象

確保它們是等於()是不夠的,因爲我可能會遇到一致性問題。所以基本上我需要一個multiton;此外,當不需要時,我不需要將這個對象保存在內存中,所以我將使用弱引用。

我設計了這樣的解決方案:

package com.example; 

public class DbEntity { 
    // a DbEntity holds a strong reference to its key, so as long as someone holds a 
    // reference to it the key won't be evicted from the WeakHashMap 
    private String key; 

    public void setKey(String key) { 
     this.key = key; 
    } 

    public String getKey() { 
     return key; 
    } 

    //other stuff that makes this object actually useful. 

} 

package com.example; 

import java.lang.ref.WeakReference; 
import java.util.WeakHashMap; 
import java.util.concurrent.locks.ReentrantLock; 

public class WeakMultiton { 

    private ReentrantLock mapLock = new ReentrantLock(); 
    private WeakHashMap<String, WeakReference<DbEntity>> entityMap = new WeakHashMap<String, WeakReference<DbEntity>>(); 

    private void fill(String key, DbEntity object) throws Exception { 
     // do slow stuff, typically fetch data from DB and fill the object. 
    } 

    public DbEntity get(String key) throws Exception { 
     DbEntity result = null; 
     WeakReference<DbEntity> resultRef = entityMap.get(key); 
     if (resultRef != null){ 
      result = resultRef.get(); 
     } 
     if (result == null){ 
      mapLock.lock(); 
      try { 
       resultRef = entityMap.get(key); 
       if (resultRef != null){ 
        result = resultRef.get(); 
       } 
       if (result == null){ 
        result = new DbEntity();     
        synchronized (result) { 
         // A DbEntity holds a strong reference to its key, so the key won't be evicted from the map 
         // as long as result is reachable. 
         entityMap.put(key, new WeakReference<DbEntity>(result)); 

         // I unlock the map, but result is still locked. 
         // Keeping the map locked while querying the DB would serialize database calls! 
         // If someone tries to get the same DbEntity the method will wait to return until I get out of this synchronized block. 
         mapLock.unlock(); 

         fill(key, result); 

         // I need the key to be exactly this String, not just an equal one!! 
         result.setKey(key); 
        } 
       } 
       } finally { 
       // I have to check since I could have already released the lock. 
       if (mapLock.isHeldByCurrentThread()){ 
        mapLock.unlock(); 
       } 
      } 
     } 
     // I synchronize on result since some other thread could have instantiated it but still being busy initializing it. 
     // A performance penality, but still better than synchronizing on the whole map. 
     synchronized (result) { 
      return result; 
     } 
    } 
} 

WeakMultiton將只在數據庫中包裝(訪問數據庫的單點)和其get(String鍵)被實例化當然是唯一的辦法檢索一個DbEntity。 現在,據我所知,這應該可行,但由於這些東西對我來說很新,所以我擔心我可能會監督關於同步或弱引用的內容! 你能發現任何缺陷或建議改進嗎?

+1

我還沒有使用可重入鎖,所以我不是100%肯定的,但並不是所有的代碼路徑都會導致你的地圖解鎖。我建議在'try'塊內移動'mapLock.lock()',然後將'maoLock.unlock()'放在'finally'塊中。至於其餘部分,我不能和任何權威機構談論它的有效性。 – jpm

+0

對,我最初把解鎖放在一個finally塊中,然後忘了爲什麼我做了它,並把它移到一個catch :) 我看不到移動鎖的嘗試,雖然... – lultimouomo

+0

我相應地更正了代碼 – lultimouomo

回答

1

我發現了番石榴的地圖製作工具,並寫了這個通用AbstractWeakMultiton:

package com.example; 

import java.util.Map; 
import java.util.concurrent.locks.ReentrantLock; 

import com.google.common.collect.MapMaker; 

public abstract class AbstractWeakMultiton<K,V, E extends Exception> { 

    private ReentrantLock mapLock = new ReentrantLock(); 
    private Map<K, V> entityMap = new MapMaker().concurrencyLevel(1).weakValues().<K,V>makeMap(); 

    protected abstract void fill(K key, V value) throws E; 
    protected abstract V instantiate(K key); 
    protected abstract boolean isNullObject(V value); 


    public V get(K key) throws E { 
     V result = null; 
     result = entityMap.get(key); 
     if (result == null){ 
      mapLock.lock(); 
      try { 
       result = entityMap.get(key); 
       if (result == null){ 
        result = this.instantiate(key);    
        synchronized (result) { 
         entityMap.put(key, result); 
         // I unlock the map, but result is still locked. 
         // Keeping the map locked while querying the DB would serialize database calls! 
         // If someone tries to get the same object the method will wait to return until I get out of this synchronized block. 
         mapLock.unlock(); 

         fill(key, result); 

        } 
       } 
      } finally { 
       // I have to check since the exception could have been thrown after I had already released the lock. 
       if (mapLock.isHeldByCurrentThread()){ 
        mapLock.unlock(); 
       } 
      } 
     } 
     // I synchronize on result since some other thread could have instantiated it but still being busy initializing it. 
     // A performance penalty, but still better than synchronizing on the whole map. 
     synchronized (result) { 
      // I couldn't have a null result because I needed to synchronize on it, 
      // so now I check whether it's a mock object and return null in case. 
      return isNullObject(result)?null:result; 
     } 
    } 
} 

它具有以下優點我先前的嘗試:

它不依賴於一個事實,即值保持很強的參考到密鑰 它不需要做過期的弱引用的尷尬雙重檢查 它是可重複使用的

另一方面,它取決於相當健壯的Guav一個庫,而第一個解決方案只使用運行時環境中的類。我可以忍受這一點。

我顯然還在尋找進一步的改進和錯誤發現,基本上所有答案都是最重要的問題:它會起作用嗎?

相關問題