【发布时间】:2018-07-17 08:32:50
【问题描述】:
我需要更新 ConcurrentHashmap 中的值,但我不确定如何确保线程安全。
Hashmap 是一个 ConcurrentHashMap,我需要获取自定义类的实例,对其执行一些操作,然后将更新后的值放回。
有没有办法将这个 get-alter-put 操作结合到原子操作?
提前致谢
【问题讨论】:
标签: java thread-safety concurrenthashmap
我需要更新 ConcurrentHashmap 中的值,但我不确定如何确保线程安全。
Hashmap 是一个 ConcurrentHashMap,我需要获取自定义类的实例,对其执行一些操作,然后将更新后的值放回。
有没有办法将这个 get-alter-put 操作结合到原子操作?
提前致谢
【问题讨论】:
标签: java thread-safety concurrenthashmap
ConcurrentHashMap 的computeIfPresent 方法是可能的。来自 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) 添加了一个新 映射。
【讨论】:
我会说你可以使用
map.compute(key, (k,v) -> {
if(k != null) {
return v+1;
} else {
return some_var;
}
});
【讨论】:
您可以使用 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);
}
【讨论】:
#computeIfPresent 几乎总是非阻塞的(考虑到良好的哈希函数)。