在 HashMap 值对象上同步
Synchronized on HashMap value object
我有一个关于 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;
}
}
我看不出您的方法与标准 ConcurrentHashMap 的方法有什么区别 - 除了 ConcurrentHashMap
已经过大量测试并且可以配置为最小开销的事实您想要 运行 代码的确切线程数。
在 ConcurrentHashMap
中,您可以使用 replace(K key, V old, V new)
方法仅当 old
值没有改变。
由于删除所有这些 AtomicIntegers
而节省的 space 以及由于较低的同步开销而节省的时间可能会补偿必须将 replace(k, old, new)
调用包装在 while 循环中:
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;
}
此外,从好的方面来说,即使密钥在检查后被删除(在这种情况下,您的密钥会抛出 NPE),此代码也能正常工作。
我有一个关于 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;
}
}
我看不出您的方法与标准 ConcurrentHashMap 的方法有什么区别 - 除了 ConcurrentHashMap
已经过大量测试并且可以配置为最小开销的事实您想要 运行 代码的确切线程数。
在 ConcurrentHashMap
中,您可以使用 replace(K key, V old, V new)
方法仅当 old
值没有改变。
由于删除所有这些 AtomicIntegers
而节省的 space 以及由于较低的同步开销而节省的时间可能会补偿必须将 replace(k, old, new)
调用包装在 while 循环中:
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;
}
此外,从好的方面来说,即使密钥在检查后被删除(在这种情况下,您的密钥会抛出 NPE),此代码也能正常工作。