在HashMap值对象上同步

时间:2015-03-09 08:38:11

标签: java concurrency hashmap

我有一个关于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;
        }
    }

1 个答案:

答案 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)。