需要Java单例解释

需要Java单例解释,java,thread-safety,singleton,Java,Thread Safety,Singleton,通常我使用第一个实现。几天前我发现了另一个。 有人能解释一下这两种实现之间的区别吗? 第二个实现是线程安全的? 在第二个示例中使用内部类的优点是什么 //--1st Impl public class Singleton{ private static Singleton _INSTANCE; private Singleton() {} public static Singleton getInstance(){ if(_INSTAN

通常我使用第一个实现。几天前我发现了另一个。 有人能解释一下这两种实现之间的区别吗? 第二个实现是线程安全的? 在第二个示例中使用内部类的优点是什么

//--1st Impl
public class Singleton{

      private static Singleton _INSTANCE;

      private Singleton() {}
      public static Singleton getInstance(){
          if(_INSTANCE == null){
               synchronized(Singleton.class){
                      if(_INSTANCE == null){
                          _INSTANCE = new Singleton();
                      }
               }
          }
      return _INSTANCE;
      }  
}

//--2nd Impl
public class Singleton {
      private Singleton() {}

      private static class SingletonHolder { 
            private static final Singleton _INSTANCE = new Singleton();
      }

      public static Singleton getInstance() {
            return SingletonHolder._INSTANCE;
      }
}

第一个实现使用了所谓的“双重检查锁”。这是一件非常糟糕的事情。它看起来是线程安全的,但实际上不是

第二种实现实际上是线程安全的


关于第一个实现为何被破坏的解释相当复杂,因此我建议您获取一份Brian Goetz的副本,以进行详细解释。短版本是允许编译器在构造函数完成之前分配
\u实例
变量,这可能会导致第二个线程看到部分构造的对象。

如果
\u实例
变得不稳定,则第一个实现仅是线程安全的。第二个是线程安全的,因为只有在类加载器加载了
SingletonHolder
之后才会初始化
\u实例

因此,当内部类在一段时间内被访问时(比整个程序加载的时间晚得多),类加载器加载内部类并初始化变量。因此,对于以后的访问,该对象随时可用 因此方法
getInstance()
是线程安全的


第二个实现的优点是,您不必担心同步或计算类装入器为您所做的事情

第一个代码段就是一个示例,它过去非常流行,但现在已经不再使用了

第二个代码段使用了类加载语义和Java语言规范定义的
final
关键字的组合,以确保延迟初始化和线程安全,因此更好。

\35; 1设计用于确保延迟初始化。但是,在给定的情况下,#2也确保了延迟初始化_实例仅在加载Singleton.class时创建,并且在第一次调用getSingleton()时加载Singleton.class。类中没有其他方法。不需要双重检查锁定。当然,在#1#中,实例应该是易变的



注意:我不同意双重检查锁定是不好的。正确实现后,它可能非常有用。

惰性初始化与非惰性初始化。您可能有兴趣检查一下这个:@HovercraftFullOfEels不,第二个也是惰性的。看,如果您真的需要单例
我感谢大家。现在我可以理解这两个实现了。不,第一个不是。双重检查锁定不是线程安全的。如果变量为volatileBetter,则为@CameronSkinner。我没有投反对票,但如果我有:)@Jatin:你最后解释了。谢谢你和卡梅隆!使用
volatile
@MattBall,第一个实现可以非常容易地在Java 5+中实现线程安全:但实际上并非如此。但这就是为什么你应该避免双重检查锁定:它太容易出错。为什么我们需要一个支架。为什么我们不能直接使用这个“私有静态最终单例_INSTANCE=new Singleton();”。它仍然是单例的,并在同一时间初始化time@Jatin因为太简单,人们害怕简单。
final
既不能保证延迟初始化,也不能保证线程安全。第二个实现是利用类加载的工作方式。@CameronSkinner:是的,但是
final
的明确赋值语义确保只分配了一个实例,并且写操作安全地发布到其他线程。这很公平<然而,代码>最终版
仍然与懒惰无关。