如何以computeIfAbsent效率实现Map putIfAbsent语义?

Dan*_*zan 6 java

考虑以下代码:

ConcurrentHashMap<String, Value> map = new ConcurrentHashMap<>();

boolean foo(String key) {
    Value value = map.get(key);
    if (value == null) {
        value = map.putIfAbsent(key, new Value());
        if (value == null) {
            // do some stuff
            return true;
        }
    }
    // do some other stuff
    return false;
 }
    
Run Code Online (Sandbox Code Playgroud)

假设foo()由多个线程并发调用。还假设呼叫new Value()是昂贵的。代码很冗长,仍然会导致Value创建冗余对象。上述逻辑能否以一种保证不Value创建冗余对象的方式实现(即new Value()最多调用一次)?我正在寻找一个干净的实现 - 最少的代码而不显式获取锁。

computeIfAbsent 可能是一个不错的选择,但是它的返回语义不符合所需的逻辑。

Mic*_*ael 1

首先,让我们解决您没有以原子方式进行操作的事实,并进行不必要的查找。两个线程可以同时通过第一次value == null检查。现在并不是真正的问题(除了Value会创建 2 秒,这很慢),但如果有人在第二次检查中添加 else 子句,就会出现错误value == null。这样也比较干净。

boolean foo(String key) {
    Value value = map.putIfAbsent(key, new Value());
    if (value == null) {
        // do some stuff
        return true;
    } 
    else {
       // do some other stuff
       return false;
    }
 }
Run Code Online (Sandbox Code Playgroud)

现在让我们解决创建速度慢的事实Value(听起来像是您在滥用构造函数,但无论如何)。

 boolean foo(String key) {
    final AtomicBoolean wasCreated = new AtomicBoolean(false);
    final Value value = map.computeIfAbsent(key, k -> {
        wasCreated.set(true);
        return new Value();
    });
    if (wasCreated.get()) {
        // do some stuff
        return true;
    } 
    else {
       // do some other stuff
       return false;
    }
 }
Run Code Online (Sandbox Code Playgroud)