2011-01-24 45 views
0

我想知道什麼是最好的方法來解決這個問題。我有一個調用Web服務,並將結果存儲在本地內存中緩存,像一個(抽象地講)簡單的方法:現在同步一個特定的web服務調用

public Document callWebservice(SomeObject parameter) { 
    Document result = cache.get(parameter); 
    if (result == null) { 
     result = parse(retrieve(parameter)); 
     cache.put(result); 
    } 
    return result; 
} 

,如果文檔是在緩存中,它可以只返回無問題很好。在單線程環境中,這種方法也可以正常工作。但是,在多線程環境中,每個線程都會轉到「其他」位,並多次調用Web服務。

我可能會在「其他」部分扔下一個synchronized塊,但我相信這是一個鎖太「寬」 - 整個法會調用,即使他們稱之爲完全不同的東西線程不可用。

它的優良如果web服務被調用兩次,只要該請求是不同的(即,SomeObject參數,在這種情況下)。

現在,問題:什麼是採取在這種情況下,最好的辦法?

我想過存儲參數在(線程)集合對象。如果參數的內容相同,它會產生相同的hashCode/equals結果,並且會在集合對象中找到,表明另一個線程已經處理了該請求。如果是這種情況,可以暫停調用線程,直到web服務返回。 (我不得不弄清楚如何讓調用線程等待)。這會在SomeObject參數對象上鎖定嗎?例如:

private Map<SomeObject, SomeObject> currentlyProcessingItems = new ConcurrentHashMap<SomeObject, SomeObject>(); 
public Document callWebservice(SomeObject parameter) { 
    if (currentlyProcessedItems.contains(parameter)) { 
     parameter = currentlyProcessedItems.get(parameter); 
    } else { 
     currentlyProcessedItems.putIfAbsent(parameter); 
    } 
    synchronized(parameter) { 
     Document result = cache.get(parameter); 
     if (result == null) { 
      Document result = parse(retrieve(parameter)); 
      cache.put(result); 
     } 
     currentlyProcessedItems.remove(parameter); 
     return result; 
    } 
} 

(注:跟蹤當前處理的請求,ConcurrentHashMap中的使用,並鎖定可能不是最優的或完全錯誤的邏輯)

不,我從來沒有真正完成了線程讀的書。我應該。

我敢肯定,這個特殊的問題是相當普遍的,我只是沒能找到答案。如果我可能會問,這種叫做什麼情況(即鎖定一個特定的物體)呢?

+0

你在SomeObject上獲得了equals和hashCode? – 2011-01-24 16:36:34

+0

是的,根據Commons Lang的HashCodeBuilder和EqualsBuilder,該參數有一個很好的`hasCode()`和`equals()`方法。它在內部也有一些對象,它也正確地實現了`hashCode()`和`equals()`。 – fwielstra 2011-01-25 14:35:20

回答

0

我自己在想這個問題,特別是當檢索(參數)需要很長時間時(對於我來說,它連接到服務器來檢查身份驗證,後端的進程/過濾請求等)。現在我還沒有嘗試過,但爲了討論的緣故,這聽起來如何?

要使用緩存,必須從線程調用新的MyCache(key).getValue(),因爲getValue將被鎖定在方法getCacheValue()中,直到該值變爲可用(對所有等待的線程)。

public class MyCache { 

    private final static HashMap cacheMap = new HashMap(); // One Map for all 
    private final static Vector fetchList = new Vector(); // One List for all 

    private Object cacheValue; 
    private boolean waitingState; 

    public MyCache (Object key) { 
     if (cacheMap.containsKey (key)) {  // somebody has done it 
      cacheValue = cacheMap.get (key); 
     } else { 
      waitingState = true; 
      if (fetchInProgress (key, false)) // someone else is doing it 
       return; 
      new Thread (new MyFetch (key)).start(); 
    }} 

    synchronized private static boolean fetchInProgress (Object key, boolean remove) { 
     if (remove) { 
      fetchList.remove (key); 
     } else { 
      boolean fetchingNow = fetchList.contains (key); 
      if (fetchingNow) 
       return true; 
      fetchList.addElement (key); 
     } 
     return false; 
    } 

    public Object getValue() { 
     if (waitingState) 
      getCacheValue (true); 
     return cacheValue; 
    } 

    synchronized private void getCacheValue (boolean waitOnLock) { 
     if (waitOnLock) { 
      while (waitingState) { 
       try { 
        wait(); 
       } catch (InterruptedException intex) { 
     }}} else { 
      waitingState = false; 
      notifyAll(); 
    }} 

    public class MyFetch implements Runnable { 
     private Object fetchKey; 
     public MyFetch (Object key) { 
      fetchKey = key;  // run() needs to know what to fetch 
     } 

     public void run() {  // Grab the resource, handle exception, etc. 
      Object fetchedValue = null; 
      // so it compiles, need to replace by callWebService (fetchKey); 
      cacheMap.put (fetchKey, fetchedValue); // Save for future use 
      fetchInProgress (fetchKey, true); // Remove from list 
      getCacheValue (false);   // Notify waiting threads 
}}} 
1

警告,在XML DOM對象線程安全的。所以(忽略高速緩存本身的線程安全問題),您不能在多個線程之間共享Document實例。我們在自己的代碼庫中得到了這一點,所以我知道這是有問題的事實(不僅僅是一些理論問題)。

爲緩存本身,我相信番石榴有你在找什麼:a computing map

0

我給我自己的想法一去,它實際上似乎做工精細 - 首先,它採用了ConcurrentHashMap (儘管一個好的併發集合會更好,還沒有找到適當的實現)來維護當前正在處理的對象列表。

在該方法本身中,它查找映射是否已經包含標識符的一個實例。如果沒有,它會嘗試使用putIfAbsent()插入標識符。如果在檢查和插入之間另一個線程插入它,則putIfAbsent方法將返回另一個線程的插入項目,並使用該項目。

然後,無論是新標識符還是現有標識符,都會啓動一個同步塊,並鎖定標識符實例。在同步塊中,查詢緩存,如果沒有返回結果,則調用Web服務。實際上,這意味着執行相同請求(由標識符標識)的所有進程只能同時處理其中的一個請求,因此,當第一個進程調用webservice時,其他進程等待排隊。當輪到他們時,結果是在緩存中,等待的線程可以從那裏檢索它。

至於文檔對象不是線程安全的,我不知道該怎麼做纔對。在我的本地機器上,它似乎沒有引起任何問題,但是這樣的事情有隨機出現在生產中的習慣。格兒。我想,必須弄清楚這一點。