简体   繁体   中英

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.

However, what about I'm perform multiple lazy initialization, and store them in a ConcurrentHashMap ? Does the Map need to be volatile too?

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:

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

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.

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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