单例实例实例化

时间:2013-06-13 17:32:15

标签: java static synchronization singleton volatile

我开始习惯Java中的关键字staticvolatile。关于我正在建设的单身课程,为什么我会看到以下设计?

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;
    }
}

第一个设计是否比第二个设计有任何优势?我能看到的唯一优势是第二个设计同步整个方法,这可能需要阻止整个方法。但我反驳的是,这种方法只有一条线,根本不会明显阻塞,对吧?

3 个答案:

答案 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 关键字确保每个类只有一个实例。

你的第二个例子有效,但它没有被懒惰地初始化。