来自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/