java单例实例实例化
我开始习惯Java中的关键字static
和volatile
。关于我正在构建的一个单身类,为什么我会看到以下设计
public class Singleton{
private static volatile Singleton _instance;
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
而不是这个
public class Singleton{
private static volatile Singleton _instance = new Singleton();
public static Singleton getInstance(){
return _instance;
}
}
第一种设计比第二种有什么优势吗?我能看到的唯一优点是第二个设计同步了整个方法,这可能需要阻塞整个方法。但我的反驳是,这个方法只有一行,不会明显阻塞,对吗
# 1 楼答案
我本想添加一条评论,但似乎还不允许我这么做
第一个例子是双重检查锁定,它在Java中不再被破坏:
双重检查锁定的想法是在需要之前推迟创建单例对象,并尽可能少地使用synchronized锁定对象
第二个示例将在类加载后(可能是在启动时)创建singleton,而不管是否使用过它
这两个示例都是线程安全的,而且从J2SE 5.0开始完全可用。这取决于你是否愿意承担创建singleton对象的成本,以及是否可以在内存中保存一个从未使用过的对象
# 2 楼答案
实际上,第二个类执行“惰性”初始化,因此,如果类的实例化可能需要很多时间,并且您不确定应用程序中是否需要这个类的实例,那么就可以使用它。在这种情况下,您可以“按需”创建一个实例。 根据多线程环境,两种设计都是线程安全的
# 3 楼答案
第一个示例对多线程应用程序很有用,但被证明是不安全的,除非使用
volatile
关键字。它被称为双重检查锁定单态模式这个想法是: -检查对象是否为
null
,如果不是,则不返回它并避免锁定。 -如果是null
,则锁定类。 -再次检查它是否为null
,因为其他线程可能在当前线程之前锁定了它。 -如果仍然为空,请实例化它使用volatile可以解决这个问题,因为它可以确保其他线程在前一个线程完成之前不会读取变量,但是,锁定和同步是占用CPU时间的昂贵操作
正确的方法是使用Bill Pugh的解决方案:
这是线程安全的,也是正确的方法。这也是一个延迟初始化,因为内部类只有在被引用时才被加载,static关键字确保每个类只有一个实例
您的第二个示例是有效的,但它并不是惰性地初始化的