[英]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.