2014-01-13 33 views
4

我在工作中遇到了這種困境,想看看是否有更好的解決方案......感覺應該有一個更簡單,更清晰的答案。在Map中實現按鍵或條帶鎖定 - 最佳方法?

目標:在關鍵級別(而不是在整個地圖級別)同時訪問具有鎖定的地圖,以確保原子性,同時儘可能少地影響性能。

我有一個需要併發的地圖。 *(已添加)隨着時間的推移,地圖將填充未知數量的條目。我有多個讀者和一個作家。作者做了「隨後檢查」,讀者做了一個簡單的get()。我需要這些原子......但只在關鍵層面。例如,如果讀者正在檢查Key X,並且作者正在寫入Key Y,我不在乎是否錯過了寫入Key Y的內容。如果讀者/作者正在使用相同的密鑰,但是我需要那就是原子。

最簡單的解決方案是鎖定整個地圖。但是這看起來會影響性能,因爲大約有10,000個密鑰會在地圖中出現。 (如果這看起來不像會損害性能,因爲Map的大小相對較小,爲了討論起見,讓我們假設Map有更多的關鍵字。)

據我所知,ConcurrentHashMap不保證我需要的「按鍵」原子行爲。

想到的下一個解決方案是擁有一個鎖對象數組。您可以根據原始密鑰的散列索引到該數組的鎖Object()中。這仍然會有一些爭用,因爲你擁有的鎖少於原有地圖中的密鑰。我知道ConcurrentHashMap在底層(striping)做類似的事情來提供併發(但不是原子性)。

有沒有更簡單的方法來執行這種類型的每個鍵或條紋鎖?

謝謝。

+0

是否提前知道所有可能的密鑰?如果是這樣,你可以在產生讀者和作者之前爲每個密鑰分配一個鎖對象。 –

+0

@AdamBliss我們並不知道所有的鑰匙。另外,如果我可以避免維護自己的鎖定對象地圖,那將是一個理想的解決方案。 – Bryan

+0

_Exactly_您需要將哪些操作設爲原子按鍵? 'ConcurrentHashMap'應該在這裏做正確的事情。例如'ConcurrentHashMap.put'是原子的。 –

回答

1

當價值生成是一個耗時的過程時,可能會出現這種擔憂。您不想鎖定整個地圖並找到缺失值,並在生成值時保持地圖鎖定。您可以在生成過程中發佈地圖,但是您可能會同時出現兩次失誤和世代。

,而不是直接存儲與鍵的值,將其存儲在參考對象中:

public class Ref<T> 
{ 
    private T value; 

    public T getValue() 
    { 
     return value; 
    } 

    public void setValue(T value) 
    { 
     this.value = value; 
    } 
} 

所以,如果你原本的Map<String, MyThing>地圖,改爲使用Map<String, Ref<MyThing>>。不要打擾併發實現,只需使用HashMap或LinkedHashMap或其他。

現在您可以鎖定地圖以查找或創建參考持有者,然後釋放地圖。之後,您可以鎖定參考以查找或創建值對象:

String key; // key you're looking up 
Map<String, Ref<MyThing>> map; // the map 

// Find the reference container, create it if necessary 
Ref<MyThing> ref; 
synchronized(map) 
{ 
    ref = map.get(key); 
    if (ref == null) 
    { 
     ref = new Ref<MyThing>(); 
     map.put(key, ref); 
    } 
} 

// Map is released at this point 

// Now get the value, creating if necessary 
MyThing result; 
synchronized(ref) 
{ 
    result = ref.getValue(); 
    if (result == null) 
    { 
     result = generateMyThing(); 
     ref.setValue(result); 
    } 
} 

// result == your existing or new object 
相關問題