从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;
}
}
以上代码是否正确且线程安全?
答案 0 :(得分:3)
不需要volatile
关键字,因为ConcurrentHashMap
是ConcurrentMap
的实现提供了以下内存一致性效果:
在将对象作为键或值放入ConcurrentMap之前的线程中的操作发生在从另一个线程中的ConcurrentMap访问或删除该对象之后的操作之前
但是,这并不是您通常希望使用并发映射的方式。一般模式如下:
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;