[英]JAVA - Concurrency and locks
我需要創建一個像這樣的哈希圖:
Map<String, List<Integer>>
我正在使用2個線程。
這是輸出映射的示例:
---------------------
| xxx | a b c d |
| yyy | a' b' c' d' |
| zzz | A B C D |
---------------------
這兩個線程都有助於創建條目。 因此,例如,線程1具有要存儲的以下數據集:
-----------------------------------
| 1 | (xxx,a) (yyy,a') (zzz,A) |
| 2 | (xxx,b) (yyy,b') (zzz,B) |
-----------------------------------
線程2具有:
| 1 | (xxx,c) (yyy,c') (zzz,C) |
| 2 | (xxx,d) (yyy,d') (zzz,D) |
-----------------------------------
因此,同時,兩個線程可以計算相同的密鑰:線程1(xxx,a)和線程2(xxx,c)。
我可以使用的最佳鎖定/解鎖機制是什么?
我嘗試了以下
ReadWriteLock lock = new ReentrantReadWriteLock();
executor.submit(() -> {
lock.writeLock().lock();
try {
sleep(1);
map.put("foo", "bar");
} finally {
lock.writeLock().unlock();
}
});
但我不想鎖定整個哈希圖。 我想創建一個鎖IF,並且僅在處理期間兩個線程應該在同一個鍵上工作時才創建。
謝謝
首先,您應該使用並發Map:
Map<String, List<Integer>> map = new ConcurrentHashMap<>();
其次, 在被多個線程訪問之前,應使用同步列表填充它,如下所示:
for (String key : keys) {
map.put(key, Collections.synchronizedList(new ArrayList<>()));
}
或者您可以像這樣及時地進行操作:
List<Integer> list = map.get(key);
if (list == null) {
map.putIfAbsent(key, Collections.synchronizedList(new ArrayList<>()));
list = map.get(key);
}
而已。 現在一切都應該是線程安全的。
注意:重要的是不要在從地圖中獲取列表之前使用列表。 由於並發,一個以上的線程可能會嘗試將丟失的列表添加到映射中,但是只有其中一個會成功。 因此,嘗試添加丟失的列表后,請始終從地圖中獲取該列表。
不確定其他答案是否會滿足您的要求,因為您需要從地圖上進行讀取(以查看鍵是否已存在),然后以原子操作進行更新。
不知道這是否是最優雅的解決方案,但是您可以使用類似的方法...
導入java.util.HashSet; 導入java.util.Map; 導入java.util.Set; 導入java.util.concurrent.ConcurrentHashMap;
public class KeyLockMap<K, V> {
private Map<K, V> map;
private Set<K> lockKeys;
public KeyLockMap() {
map = new ConcurrentHashMap<K, V>();
lockKeys = new HashSet<K>();
}
public void lock(K key) throws InterruptedException {
synchronized(lockKeys) {
// Keep waiting while the lockKeys set contains the key we want to update
while(lockKeys.contains(key)) {
lockKeys.wait();
}
}
}
public void unlock(K key) {
synchronized(lockKeys) {
lockKeys.remove(key);
// Notify any threads that may be waiting for this key to be removed
lockKeys.notifyAll();
}
}
public Map<K, V> getMap() {
return map;
}
}
現在在您的線程中,您可以執行...
keyLockMap.lock(KEY);
Map<String, List<Integer>> map = keyLockMap.getMap();
...do updates...
keyLockMap.unlock(KEY);
希望這可以幫助。
另一種可行的方法是在ConcurrentHashMap上使用“ putIfAbsent”,例如...
List<Integer> newValue = new CopyOnWriteArrayList<>();
List<Integer> value = concurrentMap.putIfAbsent(KEY, newValue);
if(value == null) {
value = newValue;
}
value.add(DATA);
如果另一個線程首先執行putIfAbsent
,則將獲得該線程創建的CopyOnWriteArrayList
,並且可以向其中添加數據,因為CopyOnWriteArrayList
是線程安全的。
(已修改為考慮到在首次認沽權時返回空值的原因...)
無需重新設計輪子, java.util.concurrent
包已經包含Map
接口的線程安全實現-ConcurrentHashMap 。
但是,您還必須同步您的列表(請參見下面的BretC注釋)。 因此,最方便的解決方案可能是使用Guava同步多圖 。 像那樣:
public static void main(String[] args) {
Multimap <String, Integer> map = Multimaps.synchronizedMultimap(HashMultimap.<String, Integer> create());
// In Thread 1
map.put("foo", 1);
//In Thread 2
map.put("foo", 2);
System.out.println(map); // prints {foo=[1, 2]}
}
您應該使用Hashtable而不是Hashmap。 Hashtable是線程安全的(已經同步),不需要再次實現鎖定/解鎖機制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.