在 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),此代码也能正常工作。