java map方法中的线程安全“如果大于则放置”

z8dt9xmd  于 2022-11-27  发布在  Java
关注(0)|答案(4)|浏览(138)

我有一个ConcurrentMap<Key, Long>,它是一个缓存,从某个键到目前为止看到的最大值。如果同时有两个值进来,我想确保较大的那个被插入到map中。最干净的方法是什么?
一个丑陋的方法是用UpdateIfBigger方法写一个LargestLong类,这可能会涉及到一些CAS循环。我看不出有什么方法可以直接在ConcurrentHashMap上做这样的CAS循环。
或者在检索之后使用ConcurrentMap<Key, AtomicLong>和CAS循环。我听说对这样的原子原语使用并发集合通常是设计缺陷的标志,但这可能是一个例外。

gv8xihay

gv8xihay1#

最好的方法是使用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它大于更新的值。
我听说对这样的原子原语使用并发集合通常是设计缺陷的标志,但这可能是一个例外。
如果你的选择是使用锁,我倾向于不同意这种情况。如果你有任何链接到该论点,我很乐意阅读它。

vwhgwdsa

vwhgwdsa2#

理想情况下,您应该从一个简单的同步块开始,确定它确实创建了一个瓶颈,然后尝试使它无锁。也许更复杂的解决方案不值得努力,或者您在其他地方有一个瓶颈?
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);
        }
    }
}
6rvt4ljy

6rvt4ljy3#

map.merge(key, value, Long::max)呢?
如果指定的键尚未与值关联或与空值关联,则将其与给定的非空值关联。否则,将关联的值替换为给定重Map函数的结果,如果结果为空值,则将其移除。在组合键的多个Map值时,此方法可能很有用。
给定一个ConcurrentMap,您可以 * 假定 * 这是线程安全的。
默认实现不保证此方法的同步或原子性属性。任何提供原子性保证的实现都必须重写此方法并记录其并发属性。特别是,子接口ConcurrentMap的所有实现都必须记录是否仅在值不存在时才原子地应用一次重新Map函数。
但假设您使用的是ConcurrentHashMap,则:
整个方法调用以原子方式执行。

2wnc66cl

2wnc66cl4#

这里的关键点是措辞“同时”我相信。没有一个值会被插入在完全相同的时间
我将创建第二个线程和一个收集所有传入数据的ConcurrentSet,该线程将从Set中选取最大值,并将值插入到缓存中。
因为没有完全相同的时间,你只能通过以固定的时间速率观察它们来优化传入的数据。

相关问题