我维护一张不同游戏的高分图。当我收到新分数时,我希望能够检查该分数是否高于当前高分,如果是,则将其设为地图中该游戏的新高分。
ConcurrentHashMap 看起来像是可行的方法,但我很惊讶地发现似乎没有一种简单的方法来原子地检查映射中的当前值并有条件地更新它。
我尝试过使用
compute
,但在我看来,没有办法说“根据我的计算结果,我想将当前条目保留在适当的位置”。我希望从计算函数返回 null
可以实现这一点,但它删除了当前条目。
我的示例代码:
import java.util.concurrent.ConcurrentHashMap;
public class ConcHashMapTest{
public static void main(String[] args){
ConcurrentHashMap<String,String> h = new ConcurrentHashMap<>();
h.put("TEST","value1");
/*
The question is, can I do an atomic CHECK and update ONLY if
I want to?
I know I can use compute, but do you HAVE to change the map
or can you return null and leave the mapping as it is?
*/
h.compute("TEST",(k,v) -> {
if (v.equals("changeme")){
return "CHANGED";
}
else {
return null;
}
});
System.out.println("Now hashmap is "+h);
}
}
返回:
Now hashmap is {}
我当然可以返回与我在那里找到的相同的值,但这可能会导致该值的大量更新?也许这就是要走的路?但我不这么认为,因为并发散列图的要点之一是允许重叠读取以提高速度,并同步更新,并且为了做到这一点,它仅在有更新时才会创建一个发生之前。所以我不想每次检查值时都进行更新?
谢谢!
编辑:哦等等,我想我有点困惑了。并发哈希图的好处是,当我只读取高分时(我经常这样做),我可以进行重叠读取。如果我想要更新的原子性,我的调用必须在某个地方同步,以便它们是连续的?
所以我认为如果我想保持不变,使用计算并更新值本身一定是正确的做法吗?我相信同步仅锁定该映射(或至少存储桶),而不是整个映射..
EDIT2:进一步思考,我不想尝试原子性。尽管我每秒可能会在游戏中获得数千个可能的高分,但真正的高分将非常罕见。两个高分在完全相同的时刻到达的机会,从而存在以下交错问题:
if (score > map.get("SCORE")){
map.put("SCORE",score);
}
非常接近于零,没有什么区别。我想我只会留下一个小(几乎是理论上的)漏洞,在那里我可能无法记录高分。
'...ConcurrentHashMap 看起来像是可行的方法,但我惊讶地发现似乎没有一种简单的方法来原子地检查映射中的当前值并有条件地更新它。 ...'
ConcurrentHashMap类针对线程操作进行了优化,因此提供的方法应该全部以原子方式运行。
'...我尝试过使用
,但在我看来,没有办法说“作为我的计算结果,我想将当前条目保留在适当的位置。”我希望从计算函数返回compute
可以实现这一点,但它删除了当前条目。 ...'
null
利用 computeIfPresent 方法。
这是一个例子。
int highScore = 200;
map.computeIfPresent("abc", (k, v) -> highScore > v ? highScore : v);
'...我相信同步仅锁定该映射(或至少存储桶),而不是整个地图.....'
这里是 computeIfPresent 方法的 OpenJDK 源代码。
至少对于这个方法,同步是在 f Node 对象上,而不是方法或实例上。
整个班级优化得相当好