2010-12-04 24 views
4

如果在並行哈希映射上進行當前操作,如何執行安全獲取? (喜歡的putIfAbsent同樣的事情)Java ConcurrentHashMap原子獲得如果存在

不好的例子,不是很線程安全(檢查然後採取行動的情況):

ConcurrentMap<String, SomeObject> concMap = new ... 

//... many putIfAbsent and remove operations 

public boolean setOption(String id, Object option){ 
    SomeObject obj = concMap.get(id); 

    if (obj != null){ 
     //what if this key has been removed from the map? 
     obj.setOption(option); 
     return true; 
    } 

    // in the meantime a putIfAbsent may have been called on the map and then this 
    //setOption call is no longer correct 

    return false; 
} 

另一個不好的例子是:

public boolean setOption(String id, Object option){ 
     if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
      return true; 
     } 
     return false; 
    } 

這裏的希望的事情就是不是通過同步它們來添加,刪除和獲取操作的瓶頸。

感謝

+0

我不明白你對「這個setOption調用」的評論 - 你指哪個setOption方法?你的代碼中有兩個。 – 2010-12-04 14:02:06

+0

這只是存儲在地圖中的一些通用對象的泛型集方法。 – cdmihai 2010-12-04 14:06:15

+0

如果鍵值對可能從地圖中刪除,則您的第二個示例可能有缺陷。這可能發生在包含測試和get之間。 – 2010-12-04 14:26:30

回答

1

你似乎試圖做的是鎖定一個鍵在多個操作。只有每個操作是原子的。這並不是簡單的方法來鎖定一個鍵,只是鎖定地圖。

但是,在「如果我刪除一個密鑰時怎麼辦」的情況下,你所能做的就是延遲刪除操作,直到setOption被調用。結果應該是一樣的。

您似乎試圖解決可能不需要解決的問題。你還沒有解釋爲什麼在一個密鑰被刪除後或者等待被刪除的時候調用setOption是不好的。

2

不要使用containsKey/get,只需調用get。如果該方法返回null那麼密鑰不存在,否則密鑰存在,並且您獲得了在get時映射到的值。

從文檔:

返回的值以指定鍵被映射,或空如果此映射包含該鍵的映射。

這是你的第二個例子應該是什麼樣子:

public boolean setOption(String id, Object option) { 

    SomeObject opt = concMap.get(id); 
    if (opt == null) 
     return false; 

    opt.setOption(option); 
    return true; 
} 
+0

但是如果get返回null,因爲在調用時該鍵不存在,但是在get調用和它返回的測試之間,該鍵被添加到映射中(從另一個線程,代碼中的其他地方)? – cdmihai 2010-12-04 14:09:41

6

get()方法上ConcurrentHashMap是原子。由於該映射不允許空值,所以get()實現「如果存在」:如果結果爲null,則該鍵不存在。

0

如果您需要做的ConcurrentMap單個按鍵多次操作,您可以使用鎖分離技術以減少爭,這裏與番石榴框架的例子:

private Striped<Lock> lock; 
    public boolean setOption(String id, Object option) { 
     try { 
     Lock lock = concMap.get(id); 
     lock.lock(); 
      if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
     return true; 
    } 
    return false; 
     } finally { 
     lock.unlock(); 
     } 
    } 

或者說,自從Java 8 :ConcurrentMap.compute是一種新原子方法,請參閱它是如何在一個鍵上完成的:

concMap.compute(keyId, (key, value) -> { 
    dosmth; ... return key; }); 

ps可能的變化是與ConcurrentMap.computeIfPresent()等。