4

我在多线程应用程序中常用的数据结构是 ConcurrentHashMap,我想在其中保存一组共享相同键的项目。为特定键值安装第一项时会出现此问题。

我一直在使用的模式是:

final ConcurrentMap<KEYTYPE, Set<VALUETYPE>> hashMap = new ConcurrentHashMap<KEYTYPE, Set<VALUETYPE>>();
// ...
Set<VALUETYPE> newSet = new HashSet<VALUETYPE>();
final Set<VALUETYPE> set = hashMap.putIfAbsent(key, newSet)
if (set != null) {
  newSet = set;
}
synchronized (newSet) {
  if (!newSet.contains(value)) {
    newSet.add(value);
  }
}

是否有更好的模式来执行此操作?这甚至是线程安全的吗?Set有没有比内部更好的类java.util.HashSet

4

2 回答 2

5

我强烈建议为此使用Google Guava库,特别是Multimap的实现。HashMultimap将是您最好的选择,但如果您需要并发更新操作,则需要使用 Multimaps.synchronizedSetMultimap() 将其包装在委托

另一种选择是使用 a ComputingMap(也来自 Guava),它是一个映射,如果从调用返回的值get(Key)不存在,它会在那里实例化。ComputingMaps 是使用MapMaker创建的。

您问题中的代码大致是:

ConcurrentMap<KEYTYPE, Set<VALUETYPE>> hashMap = new MapMaker()
                 .makeComputingMap(
        new Function<KEYTYPE, VALUETYPE>() {
         public Graph apply(KEYTYPE key) {
           return new HashSet<VALUETYPE>();
         }
       });

仅当对特定键的调用将返回 null时Function才会调用。get()这意味着您可以这样做:

hashMap.get(key).put(value);

HashSet<VALUETYPE>如果它尚不存在,则安全地知道它是创建的。

MapMaker也是相关的,因为它为您提供了对返回 Map 的调整的控制,让您可以指定,例如,使用方法的并发级别concurrencyLevel()。您可能会发现这很有用:

指导更新操作之间允许的并发。用作内部尺寸调整的提示。该表在内部进行了分区,以尝试允许指定数量的并发更新而不会发生争用。由于对这些分区的条目分配不一定是统一的,因此观察到的实际并发性可能会有所不同。

于 2012-03-22T12:34:32.760 回答
0

我认为使用java.util.concurrent.ConcurrentSkipListMapandjava.util.concurrent.ConcurrentSkipListSet可以帮助您解决并发问题。

于 2012-03-22T12:35:14.110 回答