【发布时间】: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。
所以我的问题是,
- 如何在多线程环境中创建测试,使
Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet)无法正常工作?或者它只是偶然地与ConcurrentHashMap一起工作? - 如果这行代码不适用于多线程环境,谁能建议我在多线程处理中提高 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