JAVA-并发和锁
我需要创建如下哈希映射:JAVA-并发和锁,java,multithreading,locking,Java,Multithreading,Locking,我需要创建如下哈希映射: Map<String, List<Integer>> for (String key : keys) { map.put(key, Collections.synchronizedList(new ArrayList<>())); } List<Integer> list = map.get(key); if (list == null) { map.putIfAbsent(key, Collectio
Map<String, List<Integer>>
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);
}
两个线程都有助于创建条目;因此,例如,线程1要存储以下数据集:
-----------------------------------
| 1 | (xxx,a) (yyy,a') (zzz,A) |
| 2 | (xxx,b) (yyy,b') (zzz,B) |
-----------------------------------
线程2具有:
因此,在同一时刻,两个线程可以计算相同的密钥:线程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();
}
}))
但我不想锁定整个哈希映射。我想创建一个锁,当且仅当处理过程中的两个线程在同一个密钥上工作时
谢谢您应该使用Hashtable而不是Hashmap。哈希表是线程安全的(已同步),无需再次实现锁定/解锁机制。不确定另一个答案是否符合您的要求,因为您需要从映射中读取(查看密钥是否已存在),然后在原子操作中更新 不确定这是否是最优雅的解决方案,但您可以使用这样的解决方案 导入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;
}
}
公共类密钥锁映射{
私人地图;
私用锁匙;
公钥锁映射(){
map=新的ConcurrentHashMap();
lockKeys=newhashset();
}
公共无效锁(K键)引发中断异常{
同步(锁匙){
//lockKeys集合包含我们要更新的密钥,请继续等待
while(锁钥匙。包含(钥匙)){
锁钥匙。等等();
}
}
}
公共无效解锁(K密钥){
同步(锁匙){
锁钥匙。取下(钥匙);
//通知可能正在等待删除此密钥的所有线程
lockKeys.notifyAll();
}
}
公共地图getMap(){
返回图;
}
}
现在在你的线程中,你可以
keyLockMap.lock(KEY);
Map<String, List<Integer>> map = keyLockMap.getMap();
...do updates...
keyLockMap.unlock(KEY);
keyLockMap.lock(钥匙);
Map Map=keyLockMap.getMap();
…做更新。。。
钥匙锁地图。解锁(钥匙);
希望这有帮助
另一种可行的方法是在ConcurrentHashMap上使用“putIfAbsent”,例如
List<Integer> newValue = new CopyOnWriteArrayList<>();
List<Integer> value = concurrentMap.putIfAbsent(KEY, newValue);
if(value == null) {
value = newValue;
}
value.add(DATA);
List newValue=newcopyonwritearraylist();
列表值=concurrentMap.putIfAbsent(键,newValue);
如果(值==null){
值=新值;
}
增值(数据);
如果另一个线程先执行putIfAbsent
,您将获得该线程创建的CopyOnWriteArrayList
,并且您将能够向其中添加数据,因为CopyOnWriteArrayList
是线程安全的
(编辑以解释第一次放置时返回的null…无需重新发明轮子,
java.util.concurrent
包已经包含Map
接口的线程安全实现
但是,您还必须同步您的列表(请参阅下面的BretC评论)。因此,最方便的解决方案可能是使用。就像这样:
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]}
}
publicstaticvoidmain(字符串[]args){
Multimap=Multimaps.synchronizedMultimap(HashMultimap.create());
//在线程1中
地图.put(foo,1);;
//在线程2中
地图.put(foo,2);;
System.out.println(map);//prints{foo=[1,2]}
}
首先,您应该使用并发映射:
Map<String, List<Integer>> map = new ConcurrentHashMap<>();
Map Map=new ConcurrentHashMap();
其次,在多线程访问之前,您应该使用同步列表填充它,如下所示:
Map<String, List<Integer>>
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);
}
for(字符串键:键){
map.put(key,Collections.synchronizedList(newarraylist());
}
或您可以像这样及时完成:
Map<String, List<Integer>>
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);
}
List List=map.get(键);
if(list==null){
map.putIfAbsent(key,Collections.synchronizedList(newArrayList());
list=map.get(key);
}
就这样。现在一切都应该是线程安全的
注意:在从地图中获取列表之前,使用列表非常重要。由于并发性,多个线程可能会尝试向映射添加缺少的列表,但只有一个线程会成功。因此,在尝试添加缺少的列表后,始终从映射中获取它。那么,我是否可以将相同的(共享的)数据结构传递给两个线程,以便它们中的每个线程都可以存储自己的部分数据?如果使用java 8,您可能只需要使用
ConcurrentHashMap
和putIfAbsent
,然后使用CopyOnWrite列表作为值……请更好地解释一下CopyOnWrite步骤?Java中引入的ConcurrentHashMap7@Panner实际上在Java5中:)@BretCvalue=concurrentMap.putIfAbsent(KEY,value)创建新密钥后,code>返回null
。因此,下一个value.add(DATA)
可能会失败。如果线程1读取映射以查看键是否存在(它不存在),然后线程2读取映射以查看是否存在相同的键(它仍然不存在),会发生什么情况。。。一个线程的put不会覆盖另一个线程吗?如果您事先不知道密钥是什么,会发生什么情况?您也可以使用map.putIfAbsent()
,跳过初始化阶段。但每次访问值之前都必须这样做。但是您可以通过尝试获取来进行优化,只有在获取失败的情况下,putIfAbsent()
。我为您提到的备选方案添加了示例代码,事先不知道密钥。@Harmlezz正如您在上一个答案中所写的,一个列表。添加(值)
应该在if
语句之外完成,这不是真的吗?@Fab我不知道你的意思<代码>列表.添加()
可以在我发布所有代码后安全地完成。您可以在获得存储在地图中的列表后添加某些内容,而不是之前。如果其他线程之前添加了一个缺少的列表,则列表的双重获取非常重要。