我开始习惯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;
}
}
第一个设计是否比第二个设计有任何优势?我能看到的唯一优势是第二个设计同步整个方法,这可能需要阻止整个方法。但我反驳的是,这种方法只有一条线,根本不会明显阻塞,对吧?
答案 0 :(得分:2)
我本来希望添加评论,但似乎我不允许这样做。
第一个例子是Double-checked locking,它在Java中不再被破坏:
从J2SE 5.0开始,此问题已得到修复。 volatile关键字现在确保多个线程正确处理单例实例。 〜http://en.wikipedia.org/wiki/Double-checked_locking
双重检查锁定的想法是在创建单个对象之前推迟,直到需要它为止,并尽可能少地锁定对象。
第二个例子会在加载类时(可能是在启动时)创建单例,无论它是否曾被使用过。
这两个示例都是线程安全的,从J2SE 5.0开始,它们完全可用。它只取决于你想要承担创建单例对象的成本,并且天气持有一个永远不会被使用的内存中的对象是可以接受的。
答案 1 :(得分:0)
实际上第二个执行“延迟”初始化,因此,如果您的类的实例化可能需要花费大量时间,并且您不确定在您的应用程序中是否需要此类的实例。在这种情况下,您可以“按需”创建实例。 根据多线程环境 - 两种设计都是线程安全的。
答案 2 :(得分:0)
第一个示例对多线程应用程序很有用,但除非您使用volatile
关键字,否则证明不安全。它被称为双重检查锁定单例模式。
这个想法是:
- 检查对象是否为null
,以便它不返回并避免锁定。
- 如果是null
,请锁定锁定类。
- 再次检查它是否为null
,因为某些其他线程可能在当前线程之前将其锁定。
- 如果它仍为null实例化它。
使用volatile修复了这个问题,因为它确保其他线程在前一个线程完成之前不会读取变量,但是,在CPU时间方面,锁定和同步是昂贵的操作。
正确的方法是使用 Bill Pugh的解决方案:
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() { }
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
它是线程安全的,并且是正确的方法。它也是一个惰性初始化,因为只有在引用它们时才加载内部类,并且 static 关键字确保每个类只有一个实例。
你的第二个例子有效,但它没有被懒惰地初始化。