java - 这是 'double check' 反模式的情况吗? (获取一个map条目,不存在则创建)

标签 java multithreading concurrency synchronization

我正在尝试做一个映射值的线程安全 getter ,如果它不存在,它也会创建该值:

private final static Map<String, MyObject> myMap = new HashMap<String, MyObject>();

public MyObject myGetter(String key) {
    // Try to retrieve the object without synchronization
    MyObject myObject = myMap.get(key);
    if (myObject != null) {
        return myObject;
    }

    // The value does not exist, so create a new object.
    synchronized (myMap) {
        myObject = myMap.get(key);
        if (myObject == null) {
            myObject = new MyObject(key);
            myMap.put(key, myObject);
        }
        return myObject;
    }
}

注意 myMap 成员变量是 final 的,其他任何地方都不能访问它。

如果不需要,我不希望创建 MyObject(我发现的一些其他模式建议可能导致为同一键创建多个模式)。

我知道 'double check' anti-pattern ,但我不确定这段代码是否适用于该反模式,因为它不是在此处创建的成员变量本身。

所以,我的问题是,这是否仍然是该反模式的一个案例,如果是:为什么?

编辑 1:

从评论来看,这里的一个解决方法是简单地接受“性能影响”(我猜影响很小),并且只将读取包含在同步块(synchronized block)中,如下所示:

private final static Map<String, MyObject> myMap = new HashMap<String, MyObject>();

public MyObject myGetter(String key) {
    synchronized (myMap) {
        MyObject myObject = myMap.get(key);
        if (myObject == null) {
            // The value does not exist, create a new object.
            myObject = new MyObject(key);
            myMap.put(key, myObject);
        }
        return myObject;
    }
}

此外,我使用的是 Java 6,因此 ConcurrentHashMap.computeIfAbsent 不是此处的选项。

最佳答案

正如评论和您的编辑所指出的,这是双重检查锁定反模式的一种形式。

正确的实现是使用ConcurrentHashMap。您在评论中列出了您使用的是 Java 6,因此无法使用 computeIfAbsent。没关系,您仍然应该使用 ConcurrentHashMap#putIfAbsent

可以做类似double-check的操作

private final static ConcurrentMap<String, MyObject> myMap =
       new ConcurrentHashMap<String, MyObject>();

public MyObject myGetter(String key) {
   MyObject ref = myMap.get(key);
   if(ref == null) {
      ref = new MyObject(key);
      MyOject put = myMap.putIfAbsent(key, ref);
      if(put != null) {
         // some other thread won put first
         ref = put;
      }
   }
   return ref;
}

在这种情况下,由于 CHM 具有非阻塞读取的特性,您仅锁定突变。

否则,您提供的编辑是线程安全(尽管效率低下)的实现。

关于java - 这是 'double check' 反模式的情况吗? (获取一个map条目,不存在则创建),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40105915/

相关文章:

java - Java的日期计算方法

java - 从纪元以来的几天获取 java.util.Calendar

c++ - 采访 : what is the difference between pthread and windows thread created by _beginthread(ex)?

go - 如何暂停和恢复goroutine?

java - 您可以将 JDBC 与代号一一起使用吗?

java - 如何获得斐波那契递归的时间

java - 为什么 Kotlin/Java 没有抢占式调度程序的选项?

c++ - 并行线程,QT

java - "compose"在并发上下文中意味着什么?

java - 限制最多 N 次并发调用 Java 中的静态方法