高性能并发 MultiMap Java/Scala

发布于 2024-09-16 20:14:36 字数 440 浏览 7 评论 0 原文

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

多重映射将被经常读取、添加和删除。

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

我需要 O(1) 来查找给定键的所有值,O(N) 可以删除,但 O(logN) 会更好。

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

编辑:这是我构建的解决方案,可在 ApacheV2 下使用: 索引(多图)

I am looking for a high-performance, concurrent, MultiMap. I have searched everywhere but I simply cannot find a solution that uses the same approach as ConcurrentHashMap (Only locking a segment of the hash array).

The multimap will be both read, added to and removed from often.

The multimap key will be a String and it's value will be arbitrary.

I need O(1) to find all values for a given key, O(N) is OK for removal, but O(logN) would be preferred.

It is crucial that removal of the last value for a given key will remove the container of values from the key, as to not leak memory.

EDIT: HERE'S THE SOLUTION I BUILT, available under ApacheV2:
Index (multimap)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

奢华的一滴泪 2024-09-23 20:14:36

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

Why not wrap ConcurrentHashMap[T,ConcurrentLinkedQueue[U]] with some nice Scala-like methods (e.g. implicit conversion to Iterable or whatever it is that you need, and an update method)?

戒ㄋ 2024-09-23 20:14:36

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

Have you tried Google Collections? They have various Multimap implementations.

烟花肆意 2024-09-23 20:14:36

akka中有一个一个虽然我没有使用过它。

There is one in akka although I haven't used it.

冰葑 2024-09-23 20:14:36

我有一个要求,我必须有一个 Map> ,其中 Map 上的插入是并发的,并且在相应的 Set 上也是并发的,但是一旦从 Map 中消耗了一个 Key ,它必须被删除,想想如果作为一个每两秒运行一次的作业,它会消耗来自特定键的整个Set,但插入是完全并发的,以便在作业时大多数值被缓冲开始,这是我的实现:

注意:我使用 Guava 的辅助类 Maps 来创建并发 Map,此外,该解决方案模拟 实践清单 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();
  }

}

I had a requirement where I had to have a Map<Comparable, Set<Comparable>> where insertion on the Map be concurrent and also on the corresponding Set, but once a Key was consumed from the Map, it had to be deleted, think if as a Job running every two seconds which is consuming the whole Set<Comparable> from an specific Key but insertion be totally concurrent so that most values be buffered when the Job kicks in, here is my implementation:

Note: I use Guava's helper class Maps to create the concurrent Maps, also, this solution emulates Java concurrency in Practice Listing 5.19:

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();
  }

}
隱形的亼 2024-09-23 20:14:36

我做了一个 ConcurrentMultiMap mixin 扩展了 mutable.MultiMap mixin 并具有 concurrent.Map[A, Set[B]] 自类型。它对每个键进行锁定,空间复杂度为 O(n),但如果您的写入量不是特别大,那么它的时间复杂度相当不错。

I made a ConcurrentMultiMap mixin which extends the mutable.MultiMap mixin and has a concurrent.Map[A, Set[B]] self type. It locks per key, which has O(n) space complexity, but its time complexity is pretty good, if you aren't particularly write-heavy.

阳光下的泡沫是彩色的 2024-09-23 20:14:36

你应该尝试 ctries 。这是pdf

you should give ctries a try. here is the pdf.

£烟消云散 2024-09-23 20:14:36

现在讨论已经晚了,但是......

当涉及高性能并发的东西时,应该准备好编写解决方案。
对于 Concurrent,细节决定成败这句话有了完整的含义。
可以实现完全并发且无锁的结构。

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

It's late for the discussion, yet...

When it comes to high performance concurrent stuff, one should be prepared to code the solution.
With Concurrent the statement the Devil is in the details has a complete meaning.
It's possible to implement the structure fully concurrent and lock-free.

Starting base would be the NonBlocking Hashtable http://sourceforge.net/projects/high-scale-lib/ and then depending how many values per key and how often need to add/remove some copy on write Object[] for values or an array based Set with semaphore/spin lock.

缱绻入梦 2024-09-23 20:14:36

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

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

I am a bit late on this topic but I think, nowadays, you can use Guava like this:

Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet)
末が日狂欢 2024-09-23 20:14:36

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

Use MultiMaps from Gauava.
Multimaps.synchronizedMultimap(HashMultimap.create())

甲如呢乙后呢 2024-09-23 20:14:36

您是否看过 Javalution ,它旨在实现实时等,当然还有高性能。

Have you taken a look to Javalution which is intended for Real time etc. and of course high performance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文