【问题标题】:How to update a Value in a ConcurrentHashMap threadsafe如何更新 ConcurrentHashMap 线程安全中的值
【发布时间】:2018-07-17 08:32:50
【问题描述】:

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

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

有没有办法将这个 get-alter-put 操作结合到原子操作?

提前致谢

【问题讨论】:

    标签: java thread-safety concurrenthashmap


    【解决方案1】:

    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)函数返回null,现有映射被移除:

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

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

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

    编辑:

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

    (3) 添加了一个 映射。

    【讨论】:

      【解决方案2】:

      我会说你可以使用

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

      【讨论】:

        【解决方案3】:

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

        但是由于 computeIfPresent 是一个昂贵的操作,因为它的原子性和一些更新操作会在 BiFunction 的计算过程中被其他线程阻塞。您可以通过对并发 hashmap 的读取操作来排除这种情况,该操作非常快,如果返回 not 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);
            }
        

        【讨论】:

        • 在 OP 的情况下,键应该存在于 Map 中,因此值检查没有意义。如果您使用不同的键,#computeIfPresent 几乎总是非阻塞的(考虑到良好的哈希函数)。
        • 我可能错了,但是 map.get 和 computeifpresent 的分离不会破坏原子性吗?在此期间地图中的值不能发生变化吗?
        • 显然没有,因为原子的read-modify-write操作还是通过computeIfPresent来实现的。
        • 但在 computeIfPresent 更新之前发生空值检查。假设值为空?如果我错了,也许你检查我的答案或者纠正我
        猜你喜欢
        • 2016-07-16
        • 1970-01-01
        • 1970-01-01
        • 2012-08-20
        • 2011-04-15
        • 2014-03-04
        • 1970-01-01
        • 2019-09-22
        • 2019-06-28
        相关资源
        最近更新 更多