Java 并发hashmap和copyonwritearraylist
我试图用ConcurrentHashMap填充一个缓存,该缓存保存键/值 我假设使用一个Java 并发hashmap和copyonwritearraylist,java,concurrency,concurrenthashmap,copyonwritearraylist,Java,Concurrency,Concurrenthashmap,Copyonwritearraylist,我试图用ConcurrentHashMap填充一个缓存,该缓存保存键/值 我假设使用一个CopyOnWriteArrayList来处理并发性,我把它作为我的键的值,但是我在下面的代码中缺少了一些东西,当多个线程执行时,它会覆盖它的值 if (testMap.get(id) == null) { CopyOnWriteArrayList<String> copyArr = new CopyOnWriteArrayList<String>(); copyArr.a
CopyOnWriteArrayList
来处理并发性,我把它作为我的键的值,但是我在下面的代码中缺少了一些东西,当多个线程执行时,它会覆盖它的值
if (testMap.get(id) == null) {
CopyOnWriteArrayList<String> copyArr = new CopyOnWriteArrayList<String>();
copyArr.add("Add Value");
testMap().putIfAbsent(id, copyArr);
} else {
testMap.put(id,testMap.get().add("Append Value"));
}
在迭代订户映射时,我没有看到值对象。大小为0。您所做的实现存在几个问题 首先,您要以非线程安全的方式检查给定密钥是否没有列表。两个线程完全可以执行
if(testMap.get(id)=null)
然后再执行if(testMap.get(id)=null)
。这不会导致密钥本身被覆盖
但是,两个列表都会生成,而且由于您在设置键之前将元素添加到不安全的if
块中的那些列表中,所以实际的键->值映射中会出现哪些元素是任何人都可以猜测的
此外,完全没有必要这样做:
testMap.put(id,testMap.get(id).add("Append Value"));
在本例中,列表实例已经在映射中,您只需获取它并添加值。请注意,这也可能会打乱您以前的密钥分配
第二个潜在问题是,您使用的是CopyOnWriteList
,它在添加新元素时创建一个新的备份数组。这里有两个后果:
- 如果有很多额外的东西,那就太贵了
- 由于
add
操作是同步的(通过ReentrantLock
),但是get
不是同步的,因此您可能会在短时间内在不同的线程中获得不同的列表内容(但是,对于添加,列表最终是一致的)。这实际上是出于设计——CopyOnWriteArrayList
面向高读/低写操作
你至少有两条出路:
- 以线程安全的方式执行
put
操作,即。
- 仅使用
putIfAbsent
- 不要向列表的本地副本添加任何值,只添加从
get
获取的值
- 如果您需要绝对一致性而不是最终一致性,那么根本不要使用
CopyOnWriteArrayList
。改为使用带有“手动”同步的普通列表。您可以将Guava的Multimap等与同步包装器一起使用,以避免麻烦(Javadoc解释了如何使用)
您需要首先检索适当的列表,然后填充它。比如:
List<String> copyArr = testMap.get(id);
if (copyArr == null) {
copyArr = new CopyOnWriteArrayList<String>();
List<String> inMap = testMap.putIfAbsent(id, copyArr);
if (inMap != null) copyArr = inMap; // already in map
}
copyArr.add("Add Value");
List copyArr=testMap.get(id);
if(copyArr==null){
copyArr=新的CopyOnWriteArrayList();
List inMap=testMap.putIfAbsent(id,copyArr);
if(inMap!=null)copyArr=inMap;//已在映射中
}
复制增值(“增值”);
这样,只有在地图上没有新列表的情况下,你才能在地图上添加一个新列表,并将你的项目添加到地图上的任何列表中。阅读-这不是一个神奇的替代品,而是一个陷阱。更重要的是,为什么不直接向ConcurrentHashMap
写入?您编写代码的方式实际上消除了使用putIfAbsent
的优势,它与if
的功能完全相同,但采用线程安全的方式。我需要检查该id的arraylist是否已经存在。如果存在,我必须将我的值添加到其中。那么您需要一个多重映射吗?好吧,那我就试着写一个答案。虽然你的评论很有意义,但没有必要进行额外的同步。这只是使用putIfAbsent返回的值的问题。@assylias:如果OP需要绝对一致性,则会出现这种情况,并且放弃使用CopyOnWriteArrayList
。但我想你指的是第一点,“synchronize”一词可能有歧义。我将编辑措辞以避免混淆,谢谢。感谢TerriblesWiftTomato突出显示未使用putIfAbsent返回对象的错误。问题解决了。@user2596957:不客气。请(以对您帮助最大的为准)将此问题标记为已解决。嗨,assyilas,是否有地图。在end@user2596957为什么?copyArr
是一个指向映射中列表的引用-因此,您应用于copyArr
的任何操作都将在映射中的列表上执行。我采用了上面的代码并实现了我的开发版本,如下所示。CopyOnWriteArrayList subscriberArr=CacheUtils.getSubscriberMap().get(syncDet.getCardNumber()); 如果(subscriberArr==null){subscriberArr=new CopyOnWriteArrayList();CopyOnWriteArrayList重构=CacheUtils.getSubscriberMap().putIfAbsent(syncDet.getCardNumber(),subscriberArr);如果(refArr!=null){subscriberArr=refArr;}}subscriberArr.add(syncDet.getSubScriber());没有。更新了上面相同的代码。你觉得那代码有什么问题吗?
List<String> copyArr = testMap.get(id);
if (copyArr == null) {
copyArr = new CopyOnWriteArrayList<String>();
List<String> inMap = testMap.putIfAbsent(id, copyArr);
if (inMap != null) copyArr = inMap; // already in map
}
copyArr.add("Add Value");