Threadsafe在map方法中“put if greater”

时间:2014-04-23 18:36:07

标签: java concurrency thread-safety

我有一个ConcurrentMap<Key, Long>,它是从某个键到目前为止看到的最大值的缓存。如果同时有两个值,我想确保将较大的值插入到地图中。最干净的方法是什么?

一种丑陋的方法是使用LargestLong方法编写一个类UpdateIfBigger,这可能涉及一些CAS循环。我没有看到直接在ConcurrentHashMap上进行CAS循环的方法。

或者在检索后使用ConcurrentMap<Key, AtomicLong>和CAS循环。我听说这通常是一个设计缺陷的标志,对这样的原子原语使用并发集合,但这可能是一个例外。

3 个答案:

答案 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中选择最大值并将值插入缓存。

由于没有完全相同的时间,您只能通过以固定的时间速率观察输入数据来优化输入数据。