我是并发领域的新手。我读到有关 Guava 的信息Cache
和MultiMap
。我寻找能够结合两者的一些可能性的东西:
- 来自
Cache
我希望在 ACCESS_TIMEOUT 和 WRITE_TIMEOUT 过期后自动删除。 - 来自
Multimap
我想要多个值与一个键关联。 - 所有这些都必须同时进行。
- 我有多个作者和多个读者。我想用随机键添加值并删除它们。
问题:是否有适合我需求的 map 实现?
更新:Striped<Lock>
解决方案
我读到的更多内容 Striped<Lock>
- 对我来说更有吸引力。但这在我脑海中引发了更多问题:
- 如果我使用类似
Striped<Lock>
的东西与 GuavaCache
已经使用ConcurrentHashMap
我可以面对死锁或性能下降的问题。我错了吗? - 如果我使用
Striped<Lock>
超过Cache
它仍然没有消除与每个键多个值相关的问题。 - 是
Striped<Lock>
在我的情况下消除使用并发映射的需要? (我想答案是肯定的)但在 GitHub 上却看到了相反的情况。
最佳答案
您可以从 Cache<SomeKey, Collection<SomeValue>>
开始(因此您仍然会过期)并使用同步集合( Collections.synchronized*()
)作为值。
但这里真正的问题是您需要对集合进行并发访问的类型:
- 操作同步就足够了,这样集合就不会被损坏,还是您需要更高级别的语义,例如
ConcurrentMap.putIfAbsent()
优惠? 您需要以原子方式对值集合执行多个操作吗?就像您需要做的那样
if (c.contains(v)) { c.remove(v); } else { c.add(v); }
你通常想把它放入
synchronized(c) { }
阻止。
如果是这样,您可能希望将集合包装在一个类中,公开这些高级语义并管理多个操作的锁以获得所需的原子性,并使用该类作为值:Cache<SomeKey, SomeValuesContainer>
.
正如评论中提到的, Striped<Lock>
可用于同步访问多个Cache
s/ConcurrentHashMap
无需施加单一锁定,即使在中等程度的争用情况下也会影响性能。
如果您需要多个Cache
s/ConcurrentHashMap
s,那就是:为什么不Peer
s(或它的包装器)实际上包含该信息吗?
1。死锁,性能
Guava 的Cache
类似于 ConcurrentHashMap
,但它不使用它。然而,两者的工作方式相同,都具有可以独立锁定的段,从而减少并发访问映射时(尤其是更新时)的争用。使用Striped<Lock>
对任何一个的访问都不会导致死锁,只有当您没有以一致的顺序锁定多个锁时才会发生死锁:这在这里不会发生,因为您将始终锁定 Lock
获取自Striped<Lock>
在调用 Cache
之前或ConcurrentHashMap
,然后锁定其段(您看不到)。
至于性能,是的,锁定是有成本的,但这实际上取决于争用级别(可以使用 Striped<Lock>
中的 number of stripes 或 concurrencyLevel
中的 Cache
进行调整)。但是,无论如何,您都需要适当的并发支持,因为没有它,您可能会得到无效结果(或损坏数据),因此您必须做一些事情(使用锁定或 lock-free algorithm )。
2。每个键有多个值
我原来的答案仍然有效。从多个问题中很难准确地了解您到底想做什么(如果您能够在一个问题中提供完整、一致的上下文,那就更好了),但我认为 您只需要对每个键的多个值进行并发修改,因此同步集合应该足够了(但您至少需要这一点)。不过,在添加访问模式时,您必须对它们进行推理,以确保它们仍然适合模型:确保您的 replaceAll*()
methods例如,锁定他们需要的东西。
3。是ConcurrentMap
仍然需要 Striped<Lock>
?
是的!尤其是 Striped<Lock>
与单个 Lock
,因为您仍然会获得不使用相同 strip 的键的并发更新(这就是 Striped<Lock>
的全部意义),因此您需要支持并发修改的数据结构。如果您使用简单的HashMap
,您很有可能在足够的负载下损坏它(例如,导致无限循环)。
关于java - 一个键具有多个值的并发映射,并在超时时自动删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22068772/