有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java单例实例实例化

我开始习惯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) 个答案

  1. # 1 楼答案

    我本想添加一条评论,但似乎还不允许我这么做

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

    As of J2SE 5.0, this problem has been fixed. The volatile keyword now ensures that multiple threads handle the singleton instance correctly. ~ http://en.wikipedia.org/wiki/Double-checked_locking

    双重检查锁定的想法是在需要之前推迟创建单例对象,并尽可能少地使用synchronized锁定对象

    第二个示例将在类加载后(可能是在启动时)创建singleton,而不管是否使用过它

    这两个示例都是线程安全的,而且从J2SE 5.0开始完全可用。这取决于你是否愿意承担创建singleton对象的成本,以及是否可以在内存中保存一个从未使用过的对象

  2. # 2 楼答案

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

  3. # 3 楼答案

    第一个示例对多线程应用程序很有用,但被证明是不安全的,除非使用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关键字确保每个类只有一个实例

    您的第二个示例是有效的,但它并不是惰性地初始化的