Java HashMap值对象上的同步
我有一个关于地图中对象的同步的问题(我后来更改了相同对象的值)。我希望以原子方式读取、检查并可能更新映射中的值,而不锁定整个映射。这是处理对象同步的有效方法吗Java HashMap值对象上的同步,java,concurrency,hashmap,Java,Concurrency,Hashmap,我有一个关于地图中对象的同步的问题(我后来更改了相同对象的值)。我希望以原子方式读取、检查并可能更新映射中的值,而不锁定整个映射。这是处理对象同步的有效方法吗 private final Map<String, AtomicInteger> valueMap = new HashMap<>(); public Response addValue(@NotNull String key, @NotNull Integer value) {
private final Map<String, AtomicInteger> valueMap = new HashMap<>();
public Response addValue(@NotNull String key, @NotNull Integer value) {
AtomicInteger currentValue = valueMap.get(key);
if (currentValue == null) {
synchronized (valueMap) {
// Doublecheck that value hasn't been changed before entering synchronized
currentValue = valueMap.get(key);
if (currentValue == null) {
currentValue = new AtomicInteger(0);
valueMap.put(key, currentValue);
}
}
}
synchronized (valueMap.get(key)) {
// Check that value hasn't been changed when changing synchronized blocks
currentValue = valueMap.get(key);
if (currentValue.get() + value > MAX_LIMIT) {
return OVERFLOW;
}
currentValue.addAndGet(value);
return OK;
}
}
private final Map valueMap=new HashMap();
公共响应addValue(@NotNull字符串键,@NotNull整数值){
AtomicInteger currentValue=valueMap.get(键);
if(currentValue==null){
已同步(valueMap){
//在输入synchronized之前,再次检查值是否未更改
currentValue=valueMap.get(键);
if(currentValue==null){
currentValue=新的原子整数(0);
valueMap.put(键、当前值);
}
}
}
已同步(valueMap.get(键)){
//更改同步块时,请检查值是否未更改
currentValue=valueMap.get(键);
如果(currentValue.get()+值>最大限制){
回流溢流;
}
currentValue.addAndGet(值);
返回OK;
}
}
我看不出您的方法与标准方法有多大区别,因为ConcurrentHashMap
已经过大量测试,并且可以配置为使用您想要运行代码的确切线程数来最小化开销
在ConcurrentHashMap
中,只有在old
值未更改时,才可以使用(K键,V旧,V新)
方法将键
自动更新为new
由于删除所有的原子整数
而节省的空间和由于较低的同步开销而节省的时间可能会弥补在while循环中包装replace(k,old,new)
调用的不足:
ConcurrentHashMap<String, Integer> valueMap =
new ConcurrentHashMap<>(16, .75f, expectedConcurrentThreadCount);
public Response addToKey(@NotNull String key, @NotNull Integer value) {
if (value > MAX_LIMIT) {
// probably should set value to MAX_LIMIT-1 before failing
return OVERFLOW;
}
boolean updated = false;
do {
Integer old = putIfAbsent(key, value);
if (old == null) {
// it was absent, and now it has been updated to value: ok
updated = true;
} else if (old + value > MAX_LIMIT) {
// probably should set value to MAX_LIMIT-1 before failing
return OVERFLOW;
} else {
updated = valueMap.replace(key, old, old+value);
}
} while (! updated);
return OK;
}
ConcurrentHashMap值映射=
新的ConcurrentHashMap(16.75f,expectedConcurrentThreadCount);
公共响应addToKey(@NotNull字符串键,@NotNull整数值){
如果(值>最大值){
//在失败之前,可能应该将值设置为MAX_LIMIT-1
回流溢流;
}
布尔更新=假;
做{
整数old=putIfAbsent(键、值);
if(old==null){
//它不存在,现在已更新为值:ok
更新=真;
}否则如果(旧+值>最大限制){
//在失败之前,可能应该将值设置为MAX_LIMIT-1
回流溢流;
}否则{
更新=值映射。替换(键、旧、旧+值);
}
}而(!更新);
返回OK;
}
此外,从好的方面来看,即使在检查密钥后将其移除,此代码也可以工作(在这种情况下,您的代码会抛出一个NPE)。您还可以使用
ConcurrentHashMap
或Collections.synchronizedMap(map)
(请参阅)同步映射。例如,在ConcurrentHashMap中更新/返回之前,如何确保在get+检查之间的值没有更改。它应该适用于更新,因为您将在更新时存储并检查上一个值,但当返回溢出时,该值有可能在返回之前已更改,除非您在代码中锁定检查和返回之间的访问权限,我不清楚,如果在没有同步的情况下访问valueMap
,那么currentValue
是否不会保存过时的值(如您所做)。putIfAbsent()
不会返回布尔值。此外,我不明白为什么您要使用get()
,检查它是否返回null
,然后使用putIfAbsent()
,而您可以完美地直接使用putIfAbsent()
:如果它返回null
,则表示该值不在映射中,否则它将返回该值。这两种情况下都为True,@magnamaga。修正了。谢谢你的输入。这似乎很好,除了获得一个初始值>MAX_LIMIT.Fixed,尽管我仍然主张将溢出值设置为MAX_LIMIT-1左右;在第一种情况下,由于溢出,addToKey(“x”,1000)
可能会留下比addToKey(“x”,10)
更小的值,这是违反直觉的。