-1

我开始习惯关键字staticvolatileJava。关于我正在构建的单例类,为什么我会看到以下设计?

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

第一个设计比第二个设计有什么优势吗?我能看到的唯一优点是第二种设计同步了整个方法,这可能需要阻塞整个方法。但我对此的反面想法是,该方法只有一行,根本不会明显阻塞,对吧?

4

3 回答 3

2

我本来希望添加评论,但似乎我还不允许这样做。

第一个例子是双重检查锁定,它在 Java 中不再被破坏:

从 J2SE 5.0 开始,此问题已得到修复。volatile 关键字现在可确保多个线程正确处理单例实例。~ http://en.wikipedia.org/wiki/Double-checked_locking

双重检查锁定的思想是在需要时才创建单例对象,并尽可能少地使用同步锁定对象。

第二个示例将在加载类后立即创建单例(可能在启动时),无论它是否曾经使用过。

这两个示例都是线程安全的,并且从 J2SE 5.0 开始完全可用。这仅取决于您是否要承担创建单例对象的成本,并且可以接受将永远不会被使用的对象保存在内存中的天气。

于 2013-06-13T17:58:21.123 回答
0

实际上,第二个执行“惰性”初始化,所以如果你的类的实例化可能需要很多时间并且你不确定你的应用程序中是否需要这个类的实例。在这种情况下,您可以“按需”创建实例。根据多线程环境 - 两种设计都是线程安全的。

于 2013-06-13T17:39:42.123 回答
0

第一个示例对多线程应用程序很有用,但被证明不安全,除非您使用volatile关键字。它被称为双重检查锁定单例模式

这个想法是: - 检查对象是否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关键字确保每个类只有一个实例。

您的第二个示例有效,但没有延迟初始化。

于 2013-06-13T17:41:32.087 回答