有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java双重检查锁定延迟初始化我需要为映射使用volatile关键字吗

Usage_in_Java & Effective Java 2开始,我知道如果惰性初始化目标是单个变量,我需要volatile关键字

但是,我要执行多个延迟初始化,并将它们存储在ConcurrentHashMap中,怎么样?这个Map也需要易变吗

下面是代码示例

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;
    }
}

上述代码正确且线程安全吗


共 (1) 个答案

  1. # 1 楼答案

    不需要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;