public class Singleton {
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private Singleton() {
//...
}
}
据说这个实现是懒惰的初始化和线程安全的。但究竟是什么保证了它的线程安全?处理线程和锁定的JLS 17未提及静态字段具有任何发生在之前的关系。我怎样才能确定初始化只发生一次并且所有线程都看到同一个实例?
答案 0 :(得分:7)
在Java Concurrency in Practice:
中有详细描述延迟初始化持有者类idiom使用一个类,其唯一目的是初始化Resource。 JVM推迟初始化ResourceHolder类,直到它为止 实际使用[JLS 12.4.1],因为资源已初始化 使用静态初始化程序,不需要额外的同步。 任何线程对getresource的第一次调用都会导致ResourceHolder 被加载和初始化,此时初始化了 资源通过静态初始化器发生。
静态初始化
静态初始化程序由JVM在类初始化时运行, 在类加载之后但在任何线程使用该类之前。 因为JVM在初始化期间获取锁[JLS 12.4.2]和 这个锁是每个线程至少获得一次以确保 class已加载,静态初始化期间进行内存写入 对所有线程自动可见。因此静态初始化 在构造期间,对象不需要显式同步 或被引用时。
答案 1 :(得分:4)
首先我们需要了解两点:
声明中包含静态修饰符的字段称为静态字段或类变量。它们与类相关联,而不是与任何对象相关联。该类的每个实例共享一个类变量,该变量位于内存中的一个固定位置
...
类的初始化包括执行静态初始化器和类中声明的静态字段(类变量)的初始化器
这意味着静态初始化程序在初始化对象类时只执行一次(实际的 Class 对象,而不是类的实例)。
对于每个类或接口 C ,都有一个唯一的初始化锁 LC 。从 C 到 LC 的映射由Java虚拟机实现决定。
现在,简单来说,当两个线程尝试初始化instance
时,获取 LC 的第一个线程是实际初始化instnace
的线程,并且因为它实现了静态地,java提供了它只发生一次的承诺。
有关初始化锁定的更多信息,请阅读JSL 17