Java ConcurrentHashMap的computeIfAbsent()方法支持基于key的“锁定”吗?

问题描述 投票:0回答:2

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent-K-java.util.function.Function-

假设我们有 10 个线程使用不同的键值调用以下代码。提供给computeIfAbsent方法的“Function”参数是否并行运行,或者computeIfAbsent将“锁定”整个表?

Map<String, String> map = new ConcurrentHashMap<>();
map.computeIfAbsent(key, K -> { // long time operation });
java concurrenthashmap
2个回答
5
投票

有两种方法可以解释这个问题。

第一个是,理论上,ConcurrentHashMap.computeIfAbsent方法的

规范
是否保证仅在正在计算的特定键上同步?这个问题的答案直接来自该方法的文档

在计算过程中,其他线程对此映射的某些尝试更新操作可能会被阻止,因此计算应该简短且简单,并且不得尝试更新此映射的任何其他映射。

这对于它是在整个映射上同步还是仅在单个键上同步是不明确的,但它没有明确承诺在计算 value-if-absent 时可以在其他线程中进行其他键的更新。它表示“某些尝试的更新操作”可能会被阻止,但不会限制哪些操作或有多少操作被阻止。所以严格的答案是,,允许一致的实现在整个地图对象上同步,并阻止所有其他更新。

问题的第二种解释是,实际上,该方法的
实现

是否仅在单个密钥上同步?这个问题的答案将取决于哪个实现,但将来自该实现的源代码。 来自

OpenJDK 8 实现

Node<K,V> f; // ... if(/* ... */) { // ... } /* ... */ else if(/* ... */) { Node<K,V> r = new ReservationNode<K,V>(); synchronized (r) { // ... } // ... } /* ... */ else { // ... synchronized (f) { // ... } // ... }

所以答案(至少如果您使用此实现)是

,实际上,该方法在代表单个键/值对而不是整个映射的对象(f

r
)上同步,因此在计算函数时不应阻止对其他键的更新。
    


0
投票
ConcurrentHashMap.computeIfAbsent

会阻止其他一些键。 下面的代码将在为

i==1
添加值后阻塞。在 OpenJDK 17 上。
public static void main(String[] args) throws Exception {
var c = new ConcurrentHashMap<Integer, Boolean>(1);
var cc = new CountDownLatch(1);
new Thread(() -> {
  c.computeIfAbsent(0, k -> {
    cc.countDown();
    System.out.println("start");
    try {
      Thread.sleep(Long.MAX_VALUE);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    return true;
  });
}).start();


cc.await(1, TimeUnit.HOURS);
System.out.println("adding");
for (int i = 1; i < 10_000; i++) {
  System.out.println("i: " + i);
  c.putIfAbsent(i, true);
}
System.out.println("done");

}

© www.soinside.com 2019 - 2024. All rights reserved.