我在多线程应用程序中常用的数据结构是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的类?

最佳答案

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

另一个选择是使用ComputingMap(也来自Guava),这是一个映射,如果从对get(Key)的调用返回的值不存在,则在该位置进行实例化。 ComputingMap是使用MapMaker创建的。

您问题中的代码大致为:

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

仅当调用特定键的Function否则返回null时,才会调用get()。这意味着您可以执行以下操作:
hashMap.get(key).put(value);

安全地知道HashSet<VALUETYPE>(如果尚不存在)已创建。
MapMaker也很重要,因为它具有控制权,可以调节返回的Map,例如,使用concurrencyLevel()方法指定并发级别。您可能会发现有用:

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

07-26 02:48