ConcurrentHashMap的compute()方法及内部操作val的逻辑是否线程安全?
ConcurrentHashMap.compute() 场景下的操作线程安全性判定
参考代码示例
concurrentHashMapInstance.compute("Name", (key, val) -> { modifyValWithComplexLogic(val); return val; });
判定逻辑与结论
首先要明确ConcurrentHashMap中compute()方法的底层锁实现:执行compute时,仅会对当前操作key所在哈希桶的头节点加独占的synchronized锁,在整个传入的映射函数(也就是上述lambda逻辑)执行完成、新值写回Map之前,其他所有线程针对同一个key的写操作(put/remove/compute/merge等)都会被阻塞,无法进入同key的逻辑分支执行。
基于这个锁机制的边界,拆分两个场景分别判定:
- 针对整个
compute()方法对val变量的操作:在同key的操作维度下是线程安全的。因为在lambda执行的全流程中,通过参数拿到的val对象,不会被其他同key的并发写操作篡改,不存在多线程同时更新这个key对应条目的竞争问题。 - 针对
modifyValWithComplexLogic(val)方法的线程安全性:无法直接默认是安全的,完全取决于方法自身的实现边界- 如果这个方法仅在当前线程栈内执行逻辑、仅修改传入
val的内部状态,且没有把val的引用泄露到可被并发访问的代码块(比如将val传给异步线程、存入全局共享集合),那它处于compute的锁保护范围内,不存在同key的并发竞争,属于安全操作。 - 如果这个方法存在跨边界的副作用:比如
val的内部状态本身就被多线程共享、方法内部访问了未做并发控制的全局资源、或是val的引用早已被其他线程持有并操作,那compute的节点级锁是覆盖不到这些场景的,这类操作就不属于线程安全。
- 如果这个方法仅在当前线程栈内执行逻辑、仅修改传入
注意常见误区:ConcurrentHashMap的compute锁是节点级的,既不做全表范围的锁保护,也不会阻止已经持有val引用的外部线程对这个val对象本身做修改,不要把Map的并发安全等同于值对象本身的线程安全。
内容的提问来源于stack exchange,提问作者J.J. Beam
相关产品推荐
相关产品推荐

