我正在使用 ConcurrentHashMap,我需要對它執行一些非原子修改 我需要在計算一個尚不存在的新元素時迭代它的所有元素,并可能在同一個 map 上進行一些其他修改。
我希望這些操作是原子的,并阻止 ConcurrentHashMap 以防止從并發派生例外。
我撰寫的解決方案是將 ConcurrentHashMap 物件與自身同步為鎖,但聲納拋出了一個主要問題,所以我不知道該解決方案是否正確
建議的代碼:
對原文的修改
public class MyClass<K, V> {
ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>();
public V get(K key) {
return map.computeIfAbsent(key, this::calculateNewElement);
}
protected V calculateNewElement(K key) {
V result;
// the following line throws the sonar issue:
synchronized(map) {
// calculation of the new element (assignating it to result)
// with iterations over the whole map
// and possibly with other modifications over the same map
}
return result;
}
}
但是這段代碼觸發了聲納的主要問題:
“多執行緒 - 在 util.concurrent 實體上執行的同步
發現錯誤:JLM_JSR166_UTILCONCURRENT_MONITORENTER
此方法對作為 java.util.concurrent 包(或其子類)中的類的實體的物件執行同步。這些類的實體有它們自己的并發控制機制,這些機制與 Java 關鍵字 synchronized 提供的同步正交。例如,在 AtomicBoolean 上同步不會阻止其他執行緒修改 AtomicBoolean。
這樣的代碼可能是正確的,但應該仔細審查和記錄,并且可能會使以后必須維護代碼的人感到困惑。"
uj5u.com熱心網友回復:
為原子更新提供了一種方法: https ://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html#compute(K,java .util.function.BiFunction)
ConcurrentHashMap 旨在允許高度并發訪問。(請參閱描述其內部作業原理的這篇文章)如果使用提供的方法(例如計算、computeIfPresent 等)對條目進行更新,則應該只鎖定條目所在的段,而不是全部。
當您鎖定整個地圖以進行更新時,您不會獲得使用這種專門資料結構的好處。這就是Sonar所抱怨的。
還有一個問題是讀者也必須進行鎖定,更新執行緒并不是唯一需要鎖定的執行緒。這種事情就是首先發明CHM的原因。
uj5u.com熱心網友回復:
感謝所有答案,我終于可以撰寫一個解決方案。
public class MyClass<K, V> {
ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>();
public V get(K key) {
V result = map.get(key);
if(result == null) {
result = calculateNewElement(key);
}
return
}
public synchronized void put(K key, V value) {
map.put(key, value);
}
protected synchronized V calculateNewElement(K key) {
V result = map.get(key);
if(result == null) {
// calculation of the new element (assignating it to result)
// with iterations over the whole map
// and possibly with other modifications over the same map
put(key, result);
}
return result;
}
}
在我的情況下,很少呼叫 put 函式,同步是可以接受的。
uj5u.com熱心網友回復:
https://www.amazon.com.tr/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601。
“并發物件不支持'客戶端鎖定'”
您可以對同步集合執行客戶端鎖定,例如。
final List<Type> synchronizedList = Collections.synchronizedList(new ArrayList<>()); //do not use another reference to internal array list and access the list using through synchronizedList reference.
在這種情況下,您可以使用;
synchronized(synchronizedList){
//do something with synchronized list.
}
注意:這可能表現不佳,即由于代碼高度序列化而引入了可伸縮性問題。(阿姆達爾定律)。
并發物件是為可擴展性而設計的。也許您可以將地圖的快照拍攝到另一個“本地”集合并對其進行操作。或者您可以直接使用地圖而不進行任何同步。(在這種情況下,可以添加或洗掉一些新元素,并且您的迭代器可能會或可能不會反映這些更改)
“ConcurrentHashMap 與其他并發集合一起,通過提供不拋出 ConcurrentModificationException 的迭代器進一步改進了同步集合類,從而消除了在迭代期間鎖定集合的需要。ConcurrentHashMap 回傳的迭代器是弱一致的,而不是快速失敗的.弱一致性迭代器可以容忍并發修改,遍歷構造迭代器時存在的元素,并且可能(但不保證)在構造迭代器后反映對集合的修改。
ConcurrentHashMap 上有一些操作允許您對特定鍵執行原子操作,例如 compute、computeIfAbsent、computeIfPresent。 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/524469.html
標籤:爪哇多线程并发
上一篇:在腳本中實作多執行緒/并行處理
下一篇:如何在python中調度concurrent.future.Threadpool.executormap()函式
