[英]Can we use Synchronized for each entry instead of ConcurrentHashMap?
這就是問題所在:我們想要一個哈希表,其條目是線程安全的。
假設我有一個<String, Long>
的哈希表,並且我想安全地增加其中一個條目線程的值:可以嗎?
HashMap<String , Long> hashTable = new HashMap<String, Long>();
然后,每當我想增加一個條目時:
Synchronized (hashTable.get("key"))
{
Long value = hashTable.get("key");
value++;
hashTable.put("key", value);
}
我認為它比ConcurrentHashMap更好,因為它僅鎖定一個條目,而不像ConcurrentHashMap使用存儲桶並將一組條目鎖定在一起。
更重要的是,我不知道如何安全地使用COncurrenHashMap對其進行遞增。 例如,我認為以下代碼不正確:
ConcurrentHashMap<String , Long> hashTable = new ConcurrentHashMap<String, Long>();
Long value = hashTable.get("key");
value++;
hashTable.put("key", value);
我認為這是不正確的,因為兩個線程可以一個接一個地讀取鍵,然后一個接一個地寫入鍵,最終得到錯誤的值。
你們覺得怎么樣?
您提出的方法不是線程安全的,因為初始hashTable.get()
操作(通過該操作獲取您要在其上進行同步的對象)相對於其他線程put()
本身與之相關聯的值本身並不同步。相同的鍵。 此外,您的代碼不考慮將新值添加到映射或從映射中刪除鍵的可能性(所謂的“結構修改”)。 如果可能發生這種情況,無論鍵如何,那么這些動作都必須相對於對地圖的所有其他訪問進行同步。
沒錯,但是, ConcurrentHashMap
也不能解決這些問題。 就其提供的單個操作而言,它是線程安全的,其中包括Map
本身未定義的某些操作,但是必須作為同步單元執行的一系列操作仍需要通過同步進行保護。
我建議使用一種稍微不同的方法:將ConcurrentHashMap
與AtomicLong
,該變量是可變的,而不是Long
:
ConcurrentHashMap<String, AtomicLong> map;
然后,即使您不確定該鍵在映射中是否已有條目,也要更新鍵的值,請執行以下操作:
AtomicLong value = map.putIfAbsent(key, new AtomicLong(0));
long updatedValue = value.incrementAndGet();
putIfAbsent()
可確保不會因沖突的put操作而破壞值對象。 使用AtomicLong
避免了將多個操作聯合同步的需要,因為只需要一次映射訪問-檢索到的值將由所有訪問它的線程共享,並且自身可以原子更新,而無需進一步訪問映射。
如果可以確定該映射已經具有給定鍵的映射,則只需執行以下操作:
AtomicLong value = map.get(key);
long updatedValue = value.incrementAndGet();
一種或另一種方式,我認為這是您所描述和隱含的操作中可以做的最好的事情。
您甚至可以考慮將這兩種方法結合起來:
AtomicLong value = map.get(key);
if (value == null) {
value = map.putIfAbsent(key, new AtomicLong(0));
}
long updatedValue = value.incrementAndGet();
假定相對很少有給定密鑰沒有映射,並且在這種情況下避免創建新的AtomicLong
。 如果未找到映射,則必須第二次訪問該映射以確保存在映射並獲取相應的值,但是如果要避免同步,此處仍然需要putIfAbsent()
,因為可能有兩個線程都試圖在大約同一時間為同一鍵添加一個映射。 當需要添加新條目時,這樣做的成本更高,但是平均而言,它的成本可能比我的第一個建議要低。 但是,與任何性能問題一樣,必須進行測試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.