java - 双重检查锁定延迟初始化 - 我是否需要为 Map 使用 volatile 关键字

标签 java

来自Usage_in_Java & Effective Java 2 ,我确实知道如果延迟初始化目标是单个变量,我需要有 volatile 关键字。

但是,我执行多个延迟初始化并将它们存储在 ConcurrentHashMap 中怎么样? Map 是否也需要具有 volatile ?

这是代码示例。

public class Utils {
    private static final Map<Integer, RemoteViews> remoteViewsMap = new ConcurrentHashMap<Integer, RemoteViews>();

    public static RemoteViews getRemoteViews(int resourceId) {
        RemoteViews remoteViews = remoteViewsMap.get(resourceId);
        if (remoteViews == null) {
            synchronized(remoteViewsMap){
                remoteViews = remoteViewsMap.get(resourceId);
                if (remoteViews == null) {
                    remoteViews = new RemoteViews("org.yccheok.gui", resourceId);
                    remoteViewsMap.put(resourceId, remoteViews);
                }
            }
        }
        return remoteViews;
    }
}

上面的代码正确且线程安全吗?

最佳答案

不需要 volatile 关键字,因为 ConcurrentHashMap 作为 ConcurrentMap 的实现提供了以下内存一致性效果:

actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread

但是,这并不是您通常希望使用并发映射的方式。一般模式如下:

Object existing = concurrentMap.get(key);
// check if this key is already present
if (existing == null) {
  Object newObject = new Object();
  existing = concurrentMap.putIfAbsent(key, newObject); // atomic operation
  // another thread might have already stored mapping for key
  if (existing == null) {
    return newObject;
  }
}
return existing;

请注意,它不能保护您免受两个线程同时调用 new Object() 的影响(如果创建新对象的成本很高,这可能会成为问题),但它允许您避免显式同步总而言之。

更新:对于双重检查锁定,在您的情况下,它应该如下所示:

Object value = concurrentMap.get(key);
if (value == null) {
  synchronized (lock) {
    value = concurrentMap.get(key);
    if (value == null) {
      value = new Object();
      concurrentMap.put(key, value);
    }
  }
}
return value;

关于java - 双重检查锁定延迟初始化 - 我是否需要为 Map 使用 volatile 关键字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22574959/

相关文章:

java - 映射与 Hibernate 的关系

java - 如何测量不受系统时钟变化影响的 Java 时间?

java - 如何使用 Spring Kafka 清除测试中的主题

java - 是否可以将输出从 jnlp 启动的应用程序重定向到文件?

java - 无限 IntStream 的长度?

javax.crypto.BadPaddingException : pad block corrupted exception

java - Selenium Webdriver 和 Selenium Ghostdriver 有什么区别?

java - Android - fragment 中两个按钮中的第二个按钮无响应

java - 如何在 mongo 查询中找到最小的数字?

java - jooq 返回具有错误值的查询