Java 在没有种族的ConcurrentMultimap上实现删除

Java 在没有种族的ConcurrentMultimap上实现删除,java,concurrency,guava,multimap,concurrenthashmap,Java,Concurrency,Guava,Multimap,Concurrenthashmap,我一直在研究编写并发的问题,我有一个由AbstractSetMultimap和MapMaker computing map支持的实现,该MapMaker computing map根据需要创建值集合,作为ConcurrentHashMap上的集合视图。通过对视图集合和各种包装器的关注,我认为这已经非常接近了 最大的问题是,当值集合变为空时,从基础映射中删除这些值集合,而不引入竞争条件,这一问题已经被尝试过了 似乎存在两种选择 将空集合留在那里。这将泄漏一些CHM,但我相信它至少是正确的 当集合

我一直在研究编写并发的问题,我有一个由AbstractSetMultimap和MapMaker computing map支持的实现,该MapMaker computing map根据需要创建值集合,作为ConcurrentHashMap上的集合视图。通过对视图集合和各种包装器的关注,我认为这已经非常接近了

最大的问题是,当值集合变为空时,从基础映射中删除这些值集合,而不引入竞争条件,这一问题已经被尝试过了

似乎存在两种选择

  • 将空集合留在那里。这将泄漏一些CHM,但我相信它至少是正确的
  • 当集合为空时,尝试乐观地移除集合,如果其中出现任何其他内容,则进行补偿。这是一个充满种族的问题,似乎根本无法解决
  • 同步values集合上的所有内容,这至少会允许此删除,但代价是初始按键查找后的并发性
  • 为了减少损失(可能,取决于使用模式?),可能需要同步值集合的创建和删除,需要检查这是否涵盖了所有内容
问题:

  • 有人知道有比这更好的实现吗?我们能否更好地编写MapMaker的各个部分,还是需要一个专门的ConcurrentHashMultimap从头开始编写
  • 如果很难在这方面做很多改进,那么在实践中,这种泄漏可能是一个很大的问题吗?值得注意的集合,如java.util.HashMap、juc.ConcurrentHashMap和ArrayDeque不会向下调整备份存储的大小,而ArrayList也不会自动这样做。只要我们把这些东西清理干净,我想知道这是否太重要了
谢谢


编辑:另请参见guava邮件列表



编辑2:我已经写了这篇文章。请参阅以获取实现。如果您真的不想泄露这个空集合,您可以尝试用每键占位符自动替换它。这样,当再次扩展时,并发添加/删除或添加/添加应该能够达到一致状态。

使用不可变集合作为值是解决/简化基本并发问题的最佳方法,因为您可以使用原子替换方法删除。不幸的是,在一般情况下,没有具有快速复制/更新配置文件的不可变集合,因此您通常需要执行非常昂贵的复制操作。

作为后续操作,以下是我在前面的讨论中忽略的关于并发多映射实现的一些细节,您已链接到这些细节

该实现遵循了您的第一个建议:在备份映射中保留空集合。以下实时查看行为使您的其他建议复杂化

Multimap<String, Integer> multimap = HashMultimap.create();
Set<Integer> set = multimap.get("foo");
multimap.put("foo", 1);
int count = set.size();   // equals 1
Multimap Multimap=HashMultimap.create();
Set=multimap.get(“foo”);
多重映射put(“foo”,1);
int count=set.size();//等于1

对于真实世界的应用程序,与collection library类不同的是,一些不完全并发的多映射可能就足够了。您可以定义自己的类来实现Multimap接口的子集或有限的并发保证选择。或者,您的应用程序逻辑可以最大限度地减少同步多映射的争用,以避免性能问题。

我之前问过同样的问题,最后实现了4种不同的实现

问题是:

impl(我称之为索引)

这可能会起作用,但会泄漏占位符值并将键固定在内存中。诚然,占位符可能比空集合小,因此这是一个改进。从安全角度来看,不可变值集合在这里当然有效,但在存在大量争用的情况下,所有的复制都会杀死它。根据我的经验,在这成为一个问题之前,需要有大量的争用,对于中等程度的争用来说,这实际上是一个明显的胜利——特别是在读占主导地位的情况下。我解决实时视图行为的方法是
multimap.get(“foo”)
返回一个
转发集
委托给真实的。因此,该集合上的每个操作都会查找参考底图,它可以在任意两个调用之间更改。我认为,这种间接方法可以处理大多数问题,但会导致在不存在键的每个操作上创建一个伪值集合。谢谢你的评论。