concurrenthashmap和copyonwritearraylist

时间:2013-09-04 05:16:25

标签: java concurrency concurrenthashmap copyonwritearraylist

我正在尝试使用ConcurrentHashMap填充一个包含键/值的缓存。

我假设使用CopyOnWriteArrayList负责并发,我将其作为我的密钥的值,但是我在下面的代码中遗漏了一些东西,当多个线程执行时它会覆盖它的值。 / p>

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"));
}

如何保护从多个线程创建CopyOnWriteArrayList的代码。

以下是根据以下建议修改后的代码版本。

CopyOnWriteArrayList<Subscriber> subscriberArr =  CacheUtils.getSubscriberMap().get(syncDet.getCardNumber());

if (subscriberArr == null) {

subscriberArr = new CopyOnWriteArrayList<Subscriber>();
CopyOnWriteArrayList<Subscriber> refArr = 

cacheUtils.getSubscriberMap().putIfAbsent(syncDet.getCardNumber(), subscriberArr);

if (refArr != null) {
 subscriberArr = refArr;
}

}
 subscriberArr.add(syncDet.getSubScriber());

在迭代订户地图时,我没有看到价值对象。大小为0。

2 个答案:

答案 0 :(得分:1)

您的实施存在一些问题。

首先,您正在以非线程安全的方式检查给定密钥是否没有列表。在任何一个线程放置密钥之前,两个线程可以完全执行if (testMap.get(id) == null) 。这不会导致密钥本身被覆盖。

但是,会生成两个列表,并且因为您在设置密钥之前在不安全的if中向这些列表添加元素 ,在实际的键 - >值映射中出现的元素是任何人的猜测。

此外,绝对没有必要:

testMap.put(id,testMap.get(id).add("Append Value"));

在这种情况下,列表实例已经在地图中,您只需get并添加值即可。请注意,这也可能会破坏您以前的键分配!


第二个,潜在的问题是您使用的是CopyOnWriteList,它会在添加新元素时创建新的支持数组。这里有两个后果:

  • 如果有很多新增内容,价格昂贵。
  • 由于add操作已同步(通过ReentrantLock),但get 不是,您可能会获得不同的列表内容短时间内不同的线程(列表最终对于添加一致)。这实际上是按设计 - CopyOnWriteArrayList适用于高读/低写操作。

你至少有两种方法:

  • 以线程安全的方式执行put次操作,即
    • 仅使用putIfAbsent
    • 不要向列表的本地副本添加任何值,只是从get获取的值。
  • 如果您需要绝对而非最终的一致性,请不要使用CopyOnWriteArrayList。使用带有“手动”同步的普通列表。你可以用例如Guava的Multimap,例如as this one,带有同步包装器以避免麻烦(Javadoc解释如何)。

答案 1 :(得分:1)

您需要首先检索相应的列表然后填充它。类似的东西:

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");

这样你只会在地图中放入一个新列表,如果还没有,你可以将你的项目添加到地图中的任何列表中。