简体   繁体   English

仔细检查了锁定的懒惰初始化-我是否需要Map的volatile关键字

[英]Double checked locking lazy Initialization - Do I need to have volatile keyword for the Map

From Usage_in_Java & Effective Java 2 , I do understand I need to have volatile keyword, if the lazy initialization target is a single variable. Usage_in_Java和Effective Java 2中 ,我知道如果延迟初始化目标是单个变量,则需要具有volatile关键字。

However, what about I'm perform multiple lazy initialization, and store them in a ConcurrentHashMap ? 但是,我要执行多个延迟初始化并将它们存储在ConcurrentHashMap呢? Does the Map need to be volatile too? Map需要波动吗?

Here's the code example. 这是代码示例。

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

Is the above code correct and thread safe? 上面的代码正确且线程安全吗?

There's no need for volatile keyword since ConcurrentHashMap being an implementation of ConcurrentMap provides the following memory consistency effect: 不需要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 在将对象作为键或值放置到ConcurrentMap中之前在线程中执行的操作-在另一个线程中从ConcurrentMap中访问或删除该对象之后执行的操作

However, that's not how you usually want to work with concurrent map. 但是,这不是您通常要使用并发映射的方式。 General pattern is as follows: 一般模式如下:

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;

Note, it doesn't protect you from two threads simultaneously calling new Object() (which can be an issue if creation of new object is expensive), but it allows you to avoid explicit synchronization alltogether. 请注意,它不能保护您免受两个线程同时调用new Object() (如果创建新对象的成本很高,则可能会出现问题),但是它可以避免所有显式同步。

Update: as for double-checked locking, in your case, it should look as follows: 更新:对于双重检查锁定,在您的情况下,应如下所示:

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM