【问题标题】:Fast MultiMap in Multi-Thread Environments多线程环境中的快速 MultiMap
【发布时间】:2021-03-26 21:05:46
【问题描述】:

我目前正在使用Guava Multimap 实现。

map = Multimaps.synchronizedSetMultimap(HashMultimap.<K, R> create());

但是我发现我的程序性能现在受到Multimaps.synchronizedSetMultimap 中使用的同步块synchronized (mutex) 的限制。

因此,我正在寻找是否有任何替代方案可以使用同步多映射,希望这有助于提高多线程环境中的程序性能。

我不介意是否施加额外的限制,例如只有一个线程用于更新(创建、修改或删除),只要我可以使用多个线程读取多图数据并同时允许写入操作。另外,在我的项目使用中,我主要是读取数据(如果时间>99%),很少写入数据(


来自High-performance Concurrent MultiMap Java/Scala,有人建议使用

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

由于我使用的是Java 7,所以我将上面的代码转换如下:

map = Multimaps.newSetMultimap(new ConcurrentHashMap<K, Collection<R>>(), new Supplier<Set<R>>() {
    public Set<R> get() {
        return Sets.newSetFromMap(new ConcurrentHashMap<R, Boolean>());
    }
});

这似乎工作得很好。但同样来自documentation,它声明如下:

当任何并发操作更新 multimap 时,multimap 不是线程安全的,即使 map 和工厂生成的实例是。并发读取操作将正常工作。要允许并发更新操作,请通过调用 synchronizedSetMultimap(com.google.common.collect.SetMultimap) 来包装多图。

但是我已经为并发读写创建了一个测试代码,它似乎可以工作(没有任何异常,如 ConcurrentModificationException)。我当前的 Java 版本是 Java 7 并使用 Guava 14.0.1。

所以我的问题是,

  1. 如何在多线程环境中创建测试,使Multimaps.newSetMultimap(new ConcurrentHashMap&lt;&gt;(), ConcurrentHashMap::newKeySet) 无法正常工作?或者它只是偶然地与 ConcurrentHashMap 一起工作?
  2. 如果这行代码不适用于多线程环境,谁能建议我在多线程处理中提高 multimap READ 性能的方法?

非常感谢。

【问题讨论】:

  • 正如ConcurrentHashMap 所说:“类似地,迭代器、拆分器和枚举在创建迭代器/枚举时或之后返回反映哈希表状态的元素。它们不会抛出 ConcurrentModificationException 。”但这并不意味着生成的 Multimap 是正确的,某些操作不会像应有的那样具有原子性。您也许可以尝试使用 ReadWriteLock 或类似的锁定结构将其包装在一个类中。
  • 或者the implementation in this answer 链接到您自己的问题对您有用吗?
  • @Hulk 感谢您的回复。我知道 ConcurrentHashMap 在迭代期间不会抛出任何异常。但是从stackoverflow.com/questions/3768554/… 开始,在我看来,如果我在每个线程上使用一个迭代器,我会没事的。真的吗?此外,虽然文档没有指定地图是最新的,即如果有新的更新,它可能会或可能不会在迭代期间反映,这对我来说也很好。
  • 可能会想出一个无锁版本,但我不知道。因此我的建议是,要么使用更细粒度的(每个键)锁定,要么使用ReadWriteLock 以允许相对有效的并发读取,但在写入时锁定,如果写入与读取相比,这可能会有所帮助。
  • github.com/google/guava/issues/135 有一些很好的背景说明为什么在 Guava 中没有“通用并发多映射”实现。

标签: java multithreading guava concurrenthashmap multimap


【解决方案1】:

你可以使用:

ConcurrentMap<K, CopyOnWriteArraySet<V>> multimap = new ConcurrentHashMap<>();

你可以实现:

class ConcurrentArraySetMultimap<K, V> implements SetMultimap<K, V> {

在上面。


如果 Javadoc 声明 Multimaps.newSetMultimap() 不是线程安全的,我不会试图证明它是错误的。我查看了实现,原因可能是在装饰的 MapSet 之上运行了额外的逻辑。

【讨论】:

    【解决方案2】:

    但是我已经为并发读写创建了一个测试代码,它似乎可以工作(没有任何异常,如 ConcurrentModificationException)。”

    不能真正测试线程安全。线程安全错误并不总是发生——它们只是偶尔发生,而且当它们发生时非常令人困惑。

    正如 Xaerxess 在 cmets 中所链接的那样,没有真正的方法可以同时支持整个 Multimap 接口并具有良好的性能。解决方案不是使用 Multimap 界面,而是拥有自己的 ConcurrentMap&lt;K, Set&lt;V&gt;&gt; 并且非常非常小心。 (这在 Java 7 中会相当困难,因为ConcurrentMap 接口更受限制,没有原子更新操作。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-10
      • 2012-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      相关资源
      最近更新 更多