我有一個高度併發的應用程序,它利用文件系統上的資源。兩個線程同時訪問同一資源的機會相當小,但如果發生這種情況,應用程序可能會顯示有線行爲。帶弱密鑰的併發映射
每個資源都可以通過String
座標矢量進行映射(捆綁在類ResourceIdentifier
中)。在我目前的解決方案,我創造了這樣資源標識符的ConcurrentMap
收集由線程使用的時候訪問資源監視器:(正確ResourceIdentifier
覆蓋equals
和hashCode
)
ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap
= new ConcurrentHashMap<>();
public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
return concurrentMap.get(resourceIdentifier);
}
當資源被訪問時,我同步由aquireMonitor
返回的監視器對象的訪問權限。就我所瞭解的ConcurrentHashMap
的實現而言,這並不是必須阻止所有線程(爲了理解實現,我讀了this blog article),並且我的應用程序可以高興地運行,而沒有併發訪問以前引入醜陋的資源之一的危險在非常罕見的情況下出現錯誤。
但是:我的應用程序管理大量資源,concurrentMap
隨運行時增長。這就是爲什麼我現在嘗試弱引用語義添加到我的應用程序(通過使用番石榴):
ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap
= new MapBuilder().weakValues().weakKeys()
.concurrencyLevel(CONCURRENCY_LEVEL).makeMap();
public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
ResourceIdentifier monitor;
do {
concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
monitor = concurrentMap.get(resourceIdentifier);
} while(monitor == null);
return monitor;
}
CONCURRENCY_LEVEL
當然是一個靜態字段。
我的想法是這樣的:每當監視器仍在被另一個線程使用時,它當然會持有對此監視器的(強)引用。因此,ConcurrentMap
中的條目不會被垃圾收集,並且可以保證當兩個線程想要訪問相同的資源時共享監視器。 (環路地址的putIfAbsent
和get
的通話之間可能的垃圾收集。)
然而,MapMaker.weakKeys
打破了條目由equals
發現和使用身份,而不是合同。
現在我想知道:有人知道該從哪裏出發嗎?或者這種方法反正是一個壞主意?作爲一個側面問題:如果我只使用weakValues
,整個條目是否會從地圖上刪除?或者,地圖總是會有其他重要參考?感謝幫助!
PS:我的第一個猜測是我應該從地圖遷移到緩存。這可能是最好的解決方案嗎?我以前從未使用過番石榴,但現在我發現對緩存的關鍵比較有同樣的限制。
PPS:我無法在文件系統上創建鎖定。 (不是我的電話。)
但是這個實現是否會從映射中移除實際存儲在其中的'Entry'對象?我認爲它會刪除條目的值,並保持地圖的污染。 –
@raphw你有兩種選擇:懶惰地通過'get'和'contains'方法刪除舊條目(參見我的'get'方法舉例),或者通過[ReferenceQueue]熱切刪除舊條目(http:// docs .oracle.com/javase/6/docs/api/java/lang/ref/ReferenceQueue.html) –