我有一个ConcurrentMap<Key, Long>
,它是从某个键到目前为止看到的最大值的缓存。如果同时有两个值,我想确保将较大的值插入到地图中。最干净的方法是什么?
一种丑陋的方法是使用LargestLong
方法编写一个类UpdateIfBigger
,这可能涉及一些CAS循环。我没有看到直接在ConcurrentHashMap
上进行CAS循环的方法。
或者在检索后使用ConcurrentMap<Key, AtomicLong>
和CAS循环。我听说这通常是一个设计缺陷的标志,对这样的原子原语使用并发集合,但这可能是一个例外。
答案 0 :(得分:4)
最好的方法是使用AtomicLong。您将获得当前值,如果它比尝试更换它更大,如果失败再试一次。
ConcurrentMap<Key, AtomicLong> map = new ConcurrentHashMap<>();
public static boolean putIfGreater(Key key, long value){
// assuming it's never null at this point
AtomicLong val = map.get(key);
long current = val.get();
for(;;){
if(value < current)
return false;
if(val.compareAndSet(current, value))
return true;
current = val.get();
}
return false;
}
在这种情况下,它首先检查它是否更大,如果是它试图设置它。如果它未能设置它(另一个线程击败该线程),那么它将再次尝试IFF它大于更新的值。
我听说它通常是使用并发设计缺陷的标志 像这样的原子原语的集合,但这可能是一个 异常。
如果你的选择是使用锁,我会倾向于在这种情况下不同意。如果您有任何指向该论点的链接,我将很乐意阅读它。
答案 1 :(得分:4)
理想情况下,您应该从一个简单的同步块开始,确定它确实创建了一个瓶颈,然后尝试自由锁定。也许更复杂的解决方案不值得努力,或者你在其他地方有瓶颈?
John的解决方案似乎更快,但是,如果这是您的计划的关键部分,您可能还想测试以下内容(仅使用ConcurrentMap's atomic methods):
ConcurrentMap<Key, Long> map = new ConcurrentHashMap<>();
public boolean putIfGreater(Key key, long value) {
Long boxedValue = Long.valueOf(value);
Long current = map.putIfAbsent(key, boxedValue);
if (current == null) {
return true;
} else {
while (true) {
if (value < current) {
return false;
}
if (map.replace(key, current, boxedValue)) {
return true;
}
current = map.get(key);
}
}
}
答案 2 :(得分:-2)
这里的关键点是我认为“同时”的措辞。没有任何值会在完全相同的时间插入。
我会创建第二个线程和ConcurrentSet来收集所有传入的数据,该线程将从Set中选择最大值并将值插入缓存。
由于没有完全相同的时间,您只能通过以固定的时间速率观察输入数据来优化输入数据。