[英]Can this class be thread safe lazy loading, or is it as unsafe as “double checked locking”?
[英]Thread safe Lazy loading of a static map
我正在尝试以线程安全的方式延迟初始化地图。 我想出了以下这个按需初始化持有人的习惯用法 :
private static class NameIndexMapHolder {
private static final Map<String, Long> NAME_INDEX_MAP;
static {
Map<String, Long> map = new HashMap<>();
map.put("John", 3424534643);
map.put("Jane", 4328759749);
NAME_INDEX_MAP = Collections.unmodifiableMap(map);
}
}
public static Map<String, Long> getNameIndexMap() {
return NameIndexMapHolder.NAME_INDEX_MAP;
}
这样行吗? 它是线程安全的吗? 根据我的阅读,这仅适用于Singletons。 我读过的唯一其他选择是双重检查锁定,它似乎有其自身的问题。
保证静态块以线程安全的方式初始化。 单例只是一个用例。 您的代码是完全线程安全的。 有关更多详细信息,请参见此讨论。
但是,您的初始化只是伪惰性的 (我自己的虚构术语)。 直到在代码中引用类后,类才会初始化(JVM进行的懒类初始化),但是从技术上讲,您的映射会立即进行初始化(首次访问类时)。 有关类加载的更多信息,请参见此讨论。
是的,这是线程安全和惰性的。
首先,让我们看一下它是否是线程安全的:
NAME_INDEX_MAP
的引用吗? 号中发生的静态初始化,当类进行初始化(其仅执行JLS 8.7 )。 类初始化使用同步来确保只有一个线程将执行初始化程序( JLS 12.4.2 )。 1:在C的初始化锁LC上同步。...
...
4:如果C的Class对象指示C已被初始化,则无需采取进一步的措施。 释放LC并正常完成。
...
如果Java虚拟机实现可以确定类的初始化已经完成,则Java虚拟机实现可以通过取消步骤1中的锁获取(并在步骤4/5中释放)来优化此过程,前提是按照Java内存模型进行操作,则在执行优化时,如果获得了锁将存在的所有先发生顺序(JLS§17.4.5)仍然存在 。
( JVMS 5.5 ,添加了重点)
那么,这是懒惰的吗? 是; 类初始化仅在第一次访问该类的任何成员之前立即发生( JLS 12.4.1 ),在这种情况下,您只有一个字段。 因此,初始化只会在您第一次访问NAME_INDEX_MAP
之前立即进行,这是您想要的惰性。
除非我要编写java <5的代码,否则我将进行双重检查锁定:
public final class Example {
private static final Object LOCK = new Object();
private static volatile Map<String, Long> NAME_INDEX_MAP;
public static Map<String, Long> getNameIndexMap() {
if (null == NAME_INDEX_MAP) {
synchronized (LOCK) {
if (null == NAME_INDEX_MAP) {
NAME_INDEX_MAP = new HashMap<>();
NAME_INDEX_MAP.put("abc", 123);
//maybe make it immutable or use a ConcurrentMap instead?
NAME_INDEX_MAP = Collections.unmodifiableMap(NAME_INDEX_MAP);
}
}
}
return NAME_INDEX_MAP;
}
}
使双重检查的锁定习惯用法适用于Java> = 5的关键是声明映射引用为volatile
,这将使JVM在约束之前进行必要的操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.