我最近偶然发现了一篇文章,其中指出了合并方法在ConcurrentHashMap中执行原子操作的重要性。以下是文章的链接:
https://www.nurkiewicz.com/2019/03/mapmerge-one-method-to-rule-them-all.html
它表示合并方法或者将新值放在给定的键下(如果不存在),或者使用给定值(UPSERT)更新现有键并对此概念进行解释。 但是,这正是put()方法的作用。
ConcurrentHashMap中的get()和put()一起使用不是线程安全的。
请指导我了解merge()如何处理get()和put()在一起的场景并在ConcurrentHashMap上进行线程安全操作?
答案 0 :(得分:4)
并发代码的一个关键概念是,两个原子操作一个接一个地执行,而另一个操作不一定与单个原子操作执行相同的操作具有相同的效果。
让我们忽略地图尚未包含值的情况,因为那是一个不太有趣的值(您仍然需要处理一个竞争条件,但另一个更有趣)。 >
因此,如果您已经具有给定键的值,则merge
基本上会变成put(key, mergeFunction(newValue, get(key))
。
如果以这种方式实施,则会遇到丢失更新的非常现实的问题:
get(key)
并获取当前值(我们称之为v1)请注意,您的代码基本上忽略(覆盖)了对v2
的更新。如果映射值用于表示计数器,则意味着您基本上已经完全忽略了一次更新,并且将得到错误的结果。
merge
提供的一种方法是将更新应用于现有值,而不必担心其他并发更新会从您的身下改变该值,而您会丢失更新。
答案 1 :(得分:2)
您缺少的一点是,merge
方法接受一个BiFunction
(一个 remappingFunction )来计算新值,而put则盲目地插入/替换一个值。
remappingFunction接收了旧值和新值,使用它们您可以执行一些计算并返回新的更新值。