我在多线程应用程序中常用的数据结构是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);
}
}
是否有更好的模式来执行此操作?这甚至是线程安全的吗?有没有比java.util.HashSet
更好的类用于内部Set
?
我强烈建议为此使用Google Guava库,特别是Multimap的实现。HashMultimap将是你最好的选择,但如果你需要并发更新选项,你需要使用Multimaps.syncdSetMultimap((将其包装在一个委托中。
另一种选择是使用ComputingMap
(也来自 Guava(,这是一个映射,如果调用 get(Key)
返回的值不存在,它就会在那里实例化。 ComputingMap
是使用地图制作器创建的。
您问题中的代码大致为:
ConcurrentMap<KEYTYPE, Set<VALUETYPE>> hashMap = new MapMaker()
.makeComputingMap(
new Function<KEYTYPE, VALUETYPE>() {
public Graph apply(KEYTYPE key) {
return new HashSet<VALUETYPE>();
}
});
仅当对特定键的get()
调用将返回 null 时,才会调用 Function
。这意味着您可以执行以下操作:
hashMap.get(key).put(value);
安全地知道HashSet<VALUETYPE>
已创建(如果尚不存在(。
MapMaker
也是相关的,因为它为您提供了对返回 Map 调整的控制,例如,允许您使用方法 concurrencyLevel()
指定并发级别。您可能会发现这很有用:
指导更新操作之间允许的并发性。用作内部大小调整的提示。该表在内部分区,以尝试允许指示数量的并发更新而不会争用。由于向这些分区分配的条目不一定是统一的,因此观察到的实际并发性可能会有所不同。
我认为使用java.util.concurrent.ConcurrentSkipListMap
和java.util.concurrent.ConcurrentSkipListSet
可以帮助您解决并发问题。