【问题标题】:High-performance Concurrent MultiMap Java/Scala高性能并发 MultiMap Java/Scala
【发布时间】:2011-04-07 19:26:37
【问题描述】:

我正在寻找一种高性能、并发的 MultiMap。我到处搜索,但我根本找不到使用与 ConcurrentHashMap 相同的方法的解决方案(仅锁定哈希数组的一部分)。

多图会经常被读取、添加和删除。

多映射键是一个字符串,它的值是任意的。

我需要 O(1) 来查找给定键的所有值,O(N) 可以删除,但 O(logN) 将是首选。

删除给定键的最后一个值将从键中删除值的容器至关重要,以免内存泄漏。

编辑:这是我构建的解决方案,在 ApacheV2 下可用: Index (multimap)

【问题讨论】:

  • 没有 O(1) 查找的 Map(除了桶算法的东西,像往常一样)。 HashMaps 的 O(cn) 非常小。
  • ziggystar,这取决于散列函数分配密钥的程度。如果它是随机的——对于任意字符串,你可以期望现代哈希——那么查找是 O(1)。这也是 HashMap Javadoc 所说的。
  • 我有一个可能包含数百万个对象的注册表,它们可能共享一些属性,我需要这些属性的索引,以便检索具有特定属性的所有对象。
  • 也许是时候在 cstheory.stackexchange.com 上搜索你的答案了?看起来您将滚动自己的数据结构...
  • @John V:这是我构建的:gist.github.com/566793

标签: java scala concurrency multimap


【解决方案1】:

为什么不用一些很好的类似 Scala 的方法来包装 ConcurrentHashMap[T,ConcurrentLinkedQueue[U]](例如,隐式转换为 Iterable 或您需要的任何东西,以及更新方法)?

【讨论】:

  • @Viktor - 如果您有 (key,value) 对,map.get(key).remove(value) 应该可以解决问题,只要您在删除键并捕获异常时将空队列留在那里作为占位符如果它不存在(包括 NPE 关闭)。如果您不能将队列作为占位符离开,那么除非您在清理垃圾时锁定整个地图,否则很难确保安全。
  • 不能离开队列,因为它会泄漏内存。我实际上正在考虑使用 ConcurrentHashMap 源并将其弯曲到我的意愿,但这似乎是一种非常粗暴的方法。
  • 这可能是最好的解决方案,您可以限制锁定整个集合仅在您找到一个空队列时发生,我不确定您是否可以摆脱编写自己的实现,或者改变你想用它来处理这个嵌套结构而不是 Multimap 的方式。
  • 您能否容忍在获取队列时锁定队列,然后在向地图发送更新以清空该 (k,v) 对时保持队列?也就是说,使用队列上的锁而不是整个地图上的锁来避免冲突? (并添加逻辑来处理您获得队列的情况,但是在您锁定它之前,它已被清空并从地图中删除 - 您所要做的就是检查何时获得锁它是非空的. 如果它为空,则假定它已被删除。)
  • 对于那些寻找 Viktor 实现的人,你可以找到它akka.util.ConcurrentMultiMap。非常感谢,维克多!
【解决方案2】:

您尝试过 Google 收藏吗?他们有各种Multimap 实现。

【讨论】:

  • 是的,但我不只是在寻找一个多图,我在寻找一个高性能的并发多图。今天早些时候我查看他们的源代码时,他们的并发多图锁定了整个地图,从而创建了一个串行结构。
  • 您对 Syncronized 的实现是正确的 - 它锁定了整个实例 - 您是否将此集合确定为性能瓶颈?
  • 是的,这是最重要的。
【解决方案3】:

one in akka虽然没用过。

【讨论】:

    【解决方案4】:

    我做了一个ConcurrentMultiMap mixin,它扩展了 mutable.MultiMap mixin 并具有 concurrent.Map[A, Set[B]] 自类型。它锁定每个键,具有 O(n) 的空间复杂度,但它的时间复杂度非常好,如果你不是特别多写的话。

    【讨论】:

      【解决方案5】:

      我有一个要求,我必须有一个Map<Comparable, Set<Comparable>>,其中 Map 上的插入和相应的 Set 上的插入是并发的,但是一旦从 Map 中消耗了一个 Key,它就必须被删除,想想如果作为每两秒运行一次的作业会消耗来自特定键的整个Set<Comparable>,但插入是完全并发的,因此当作业启动时大多数值都会被缓冲,这是我的实现:

      注意:我使用 Guava 的辅助类 Maps 来创建并发 Maps,此外,该解决方案模拟了实践清单 5.19 中的 Java 并发

      import com.google.common.collect.MapMaker;
      import com.google.common.collect.Sets;
      
      import java.util.Collection;
      import java.util.Set;
      import java.util.concurrent.ConcurrentMap;
      
      /**
       * A general purpose Multimap implementation for delayed processing and concurrent insertion/deletes.
       *
       * @param <K> A comparable Key
       * @param <V> A comparable Value
       */
      public class ConcurrentMultiMap<K extends Comparable, V extends Comparable>
      {
        private final int size;
        private final ConcurrentMap<K, Set<V>> cache;
        private final ConcurrentMap<K, Object> locks;
      
        public ConcurrentMultiMap()
        {
          this(32, 2);
        }
      
        public ConcurrentMultiMap(final int concurrencyLevel)
        {
          this(concurrencyLevel, 2);
        }
      
        public ConcurrentMultiMap(final int concurrencyLevel, final int factor)
        {
          size=concurrencyLevel * factor;
          cache=new MapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(concurrencyLevel).makeMap();
          locks=new MapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(concurrencyLevel).weakKeys().weakValues().makeMap();
        }
      
        private Object getLock(final K key){
          final Object object=new Object();
          Object lock=locks.putIfAbsent(key, object);
          if(lock == null){
            lock=object;
          }
          return lock;
        }
      
        public void put(final K key, final V value)
        {
          synchronized(getLock(key)){
            Set<V> set=cache.get(key);
            if(set == null){
              set=Sets.newHashSetWithExpectedSize(size);
              cache.put(key, set);
            }
            set.add(value);
          }
        }
      
        public void putAll(final K key, final Collection<V> values)
        {
          synchronized(getLock(key)){
            Set<V> set=cache.get(key);
            if(set == null){
              set=Sets.newHashSetWithExpectedSize(size);
              cache.put(key, set);
            }
            set.addAll(values);
          }
        }
      
        public Set<V> remove(final K key)
        {
          synchronized(getLock(key)){
            return cache.remove(key);
          }
        }
      
        public Set<K> getKeySet()
        {
          return cache.keySet();
        }
      
        public int size()
        {
          return cache.size();
        }
      
      }
      

      【讨论】:

        【解决方案6】:

        你应该试试ctries。这是pdf

        【讨论】:

        • Aleks 下周来办公室拜访我,到时我会和他谈谈。
        • 我很想知道谈话进展如何。你觉得 ctries 有用吗?
        • 不是为了多地图的目的,我与 Bagwell 和 Prokopec 讨论了制作一个可以作为多地图工作的实现,但我认为没有足够的时间。 Ctries 将进入 Scala 2.10
        【解决方案7】:

        讨论已经很晚了,但是......

        当涉及到高性能并发的东西时,应该准备好编写解决方案的代码。 在 Concurrent 中,Devil is in the details 语句具有完整的含义。 可以实现完全并发和无锁的结构。

        起始基数将是非阻塞哈希表http://sourceforge.net/projects/high-scale-lib/,然后取决于每个键有多少值以及在写入对象[] 时需要添加/删除一些副本的频率或基于数组的带有信号量/自旋锁的集合。

        【讨论】:

          【解决方案8】:

          我在这个话题上有点晚了,但我认为,现在,你可以像这样使用 Guava:

          Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet)
          

          【讨论】:

          • Multimaps.newSetMultimap(...) 支持并发更新吗?根据 Javadoc,“当任何并发操作更新 multimap 时,multimap 不是线程安全的,即使 map 和工厂生成的实例是线程安全的。”参看。 google.github.io/guava/releases/23.0/api/docs/com/google/common/…
          • 所以通过阅读文档,之前的答案会起作用吗? : Multimaps.synchronizedSetMultimap(Multimaps.newSetMultimap(new ConcurrentHashMap(), ConcurrentHashMap::newKeySet));
          【解决方案9】:

          使用 Gauava 的 MultiMaps。 Multimaps.synchronizedMultimap(HashMultimap.create())

          【讨论】:

          • 您的解决方案是synchronized。请注意 OP 正在寻找什么。
          【解决方案10】:

          您是否看过Javalution,它用于实时等,当然还有高性能。

          【讨论】:

          • 看不到多图,更不用说并发和高性能的了:(
          猜你喜欢
          • 1970-01-01
          • 2016-05-17
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-24
          • 2010-10-31
          相关资源
          最近更新 更多