0

我正在尝试使用 ConcurrentHashMap 填充保存键/值的缓存。

我假设使用 aCopyOnWriteArrayList来处理并发性,并且我将它作为我的键的值,但是我在下面的代码中遗漏了一些东西,并且当多个线程正在执行时它会覆盖它的值。

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 。

4

2 回答 2

1

您所做的实施存在一些问题。

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

但是,这两个列表都是生成的,并且由于您在设置 key 之前if将元素添加到 unsafe块中的这些列表中,因此任何人都可以猜测实际 key->value 映射中的元素。

此外,绝对没有必要这样做:

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

在这种情况下,列表实例已经在地图中,您只需要get添加它并添加值。请注意,这也可能会弄乱您之前的按键分配!


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

  • 如果有很多添加,那就很贵了。
  • 由于add操作是同步的(通过 a ReentrantLock),但get 不是同步的,因此您可能会在短时间内在不同的线程中获得不同的列表内容(但是对于 add 而言,列表最终是一致的)。这实际上是设计使然 -CopyOnWriteArrayList面向高读/低写操作。

你至少有两种方法:

  • put以线程安全的方式 进行操作,即
    • 仅使用putIfAbsent.
    • 不要向列表的本地副本添加任何值,只需从get.
  • 如果您需要绝对一致性而不是最终一致性,请不要使用 a CopyOnWriteArrayList。改用带有“手动”同步的普通列表。您可以使用 Guava 的 Multimap,例如this one,与同步包装器一起使用,以节省您的麻烦(Javadoc 解释了如何)。
于 2013-09-04T06:50:32.367 回答
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");

这样,如果还没有一个新列表,您只需在地图中放置一个新列表,然后将您的项目添加到任何已添加到地图的列表中。

于 2013-09-04T07:34:47.180 回答