我有一个ConcurrentMap<Key, Long>
,它是一个缓存,从某个键到目前为止看到的最大值。如果同时有两个值进来,我想确保较大的那个被插入到map中。最干净的方法是什么?
一个丑陋的方法是用UpdateIfBigger
方法写一个LargestLong
类,这可能会涉及到一些CAS循环。我看不出有什么方法可以直接在ConcurrentHashMap
上做这样的CAS循环。
或者在检索之后使用ConcurrentMap<Key, AtomicLong>
和CAS循环。我听说对这样的原子原语使用并发集合通常是设计缺陷的标志,但这可能是一个例外。
4条答案
按热度按时间gv8xihay1#
最好的方法是使用AtomicLong。你会得到当前值,如果它大于,尝试替换它,如果失败,再试一次。
在这种情况下,它首先检查它是否更大,如果是,它尝试设置它。如果它没有设置它(另一个线程击败了那个线程),那么它将再次尝试IFF它大于更新的值。
我听说对这样的原子原语使用并发集合通常是设计缺陷的标志,但这可能是一个例外。
如果你的选择是使用锁,我倾向于不同意这种情况。如果你有任何链接到该论点,我很乐意阅读它。
vwhgwdsa2#
理想情况下,您应该从一个简单的同步块开始,确定它确实创建了一个瓶颈,然后尝试使它无锁。也许更复杂的解决方案不值得努力,或者您在其他地方有一个瓶颈?
John的解决方案似乎更快,但是如果这是程序的关键部分,您可能还需要测试以下代码(仅使用ConcurrentMap's atomic methods):
6rvt4ljy3#
那
map.merge(key, value, Long::max)
呢?如果指定的键尚未与值关联或与空值关联,则将其与给定的非空值关联。否则,将关联的值替换为给定重Map函数的结果,如果结果为空值,则将其移除。在组合键的多个Map值时,此方法可能很有用。
给定一个
ConcurrentMap
,您可以 * 假定 * 这是线程安全的。默认实现不保证此方法的同步或原子性属性。任何提供原子性保证的实现都必须重写此方法并记录其并发属性。特别是,子接口ConcurrentMap的所有实现都必须记录是否仅在值不存在时才原子地应用一次重新Map函数。
但假设您使用的是
ConcurrentHashMap
,则:整个方法调用以原子方式执行。
2wnc66cl4#
这里的关键点是措辞“同时”我相信。没有一个值会被插入在完全相同的时间。
我将创建第二个线程和一个收集所有传入数据的ConcurrentSet,该线程将从Set中选取最大值,并将值插入到缓存中。
因为没有完全相同的时间,你只能通过以固定的时间速率观察它们来优化传入的数据。