需要Java Singleton解释

时间:2012-12-01 04:23:19

标签: java thread-safety singleton

通常我使用第一个实现。几天前我发现了另一个。 任何人都可以解释这两个实现之间的区别吗? 第二个实现是线程安全的吗? 在第二个例子中使用内部类有什么好处?

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

4 个答案:

答案 0 :(得分:7)

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

第二种实现确实是线程安全的。

为什么第一个实现被破坏的解释相当复杂,因此我建议您获取Brian Goetz Java Concurrency in Practice的副本以获得详细说明。简短版本是允许编译器在构造函数完成之前分配_INSTANCE变量,这可能导致第二个线程看到部分构造的对象。

答案 1 :(得分:3)

如果_INSTANCE变为volatile,则第一个实现仅是线程安全的。第二个是线程安全的,因为_INSTANCE仅在类加载器加载SingletonHolder时初始化。

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

第二个实现的美妙之处在于,您不必担心同步或计数,因为类加载器会为您执行此操作

答案 2 :(得分:1)

#1旨在确保延迟初始化。但是,在给定的情况下,#2也确保了延迟初始化。只有在加载Singleton.class时才会创建_INSTANCE,并且在第一次调用getSingleton()时会加载Singleton.class。班上没有其他方法。无需双重检查锁定。当然,#1 _INSTANCE应该是不稳定的。

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

答案 3 :(得分:0)

第一个代码段是double-checked locking idiom的示例,过去非常受欢迎,但现在是known to be unsafe,不应该使用。

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