我有一个关于Map内部对象同步的问题(我后来更改了相同的对象的值)。我想原子地读取,检查并可能更新地图中的值而不锁定整个地图。这是使用对象同步的有效方法吗?
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;
}
}
答案 0 :(得分:1)
我没有看到你的方法和标准ConcurrentHashMap的方法之间存在很大差异 - 除了ConcurrentHashMap
已经过严格测试的事实,并且可以配置为具有精确的最小开销要运行代码的线程数。
在ConcurrentHashMap
中,只有当(K key, V old, V new)
值具有key
时,您才会使用replace new
方法将old
原子地更新为AtomicIntegers
没有改变。
由于删除所有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;
}
调用包含在while循环中:
{{1}}
此外,从正面来看,即使在检查密钥后删除了密钥,此代码也能正常工作(在这种情况下,您的密钥会抛出NPE)。