如何更新 ConcurrentHashMap 线程安全中的值

How to update a Value in a ConcurrentHashMap threadsafe

我需要更新 ConcurrentHashmap 中的一个值,但我不确定如何确保线程安全。

Hashmap 是一个 ConcurrentHashMap,我需要获取自定义的实例 class,对其执行一些操作,然后将更新后的值放回。

有什么方法可以将这个 get-alter-put 操作组合成原子操作吗?

提前致谢

ConcurrentHashMapcomputeIfPresent方法是一种可能。来自 javadocs:如果存在指定键的值,则尝试在给定键及其当前映射值的情况下计算新映射。整个方法调用以原子方式执行(期望计算应该简短)。

一般来说,该方法是如何工作的?一些示例代码:

考虑具有键和值的 Map<String, Integer> map{four=4, one=1, ten=10, two=2, three=3, five=5, eleven=11}

(1) 用新值更新映射(注意 lambda 是 BiFunction 返回新计算的值):

map.computeIfPresent("ten", (k, v) -> new Integer(100));

(2)函数returns为null,现有映射被移除:

map.computeIfPresent("eleven", (k, v) -> null);

(3) 不添加映射,因为不存在映射:

map.computeIfPresent("twenty", (k, v) -> new Integer(20));

编辑:

关于 compute() 的注意事项:使用相同的输入 map 数据(和方法参数),compute 方法的工作方式类似,但对于情况 3。请注意情况 3,其中 new可以添加映射:

(3) 添加了一个 new 映射。

您可以使用 ConcurrentHashMaps computeIfPresent https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfPresent-K-java.util.function.BiFunction-

但是由于 computeIfPresent 是一个代价高昂的操作,因为它是原子操作,并且在 BiFunction 的计算过程中,一些更新操作将被其他线程阻塞。您可以通过对并发 hashmap 的读取操作来排除这种情况,这非常快,如果 returns 不是 null ,则调用 computeIfPrsent

下面的代码获取键“1”的值并将其加 100 并以原子方式放回地图

ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
        map.put(1,100);
        Integer value = map.get(1);
        if (value != null)
            map.computeIfPresent(1, (key, oldValue) -> oldValue + 100);
    }

我会说你可以使用

map.compute(key,  (k,v) -> {
                      if(k != null) {
                        return v+1;
                      } else {
                        return some_var;
                      }
                    });