【问题标题】:Singleton lazy vs eager instantiation单例惰性与渴望实例化
【发布时间】:2011-12-09 01:32:31
【问题描述】:

如果单例实现如下,

class Singleton {
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

此实现与延迟初始化方法有何不同? 在这种情况下,将在加载类时创建实例,并且仅在第一次主动使用时才加载类本身(例如,Singleton.getInstance() 而不是在您声明实例时 Singleton singleton = null;)

即使使用惰性初始化方法,实例也是在调用 getInstance() 时创建的

我错过了什么吗?

【问题讨论】:

标签: java singleton lazy-initialization


【解决方案1】:

通过延迟初始化,您只在需要时创建实例,而不是在加载类时。因此,您可以避免不必要的对象创建。话虽如此,还有其他事情需要考虑。 在惰性初始化中,您提供一个公共 API 来获取实例。在多线程环境中,避免不必要的对象创建带来了挑战。您放置了造成不必要锁定的同步块,以检查已创建的对象。所以在这种情况下它成为一个性能问题。

因此,如果您确定创建对象不会占用任何大量内存并且它几乎总是会在您的应用程序中使用,那么最好在静态初始化中创建。另外请不要忘记在这种情况下使您的实例成为最终实例,因为它可以确保对象创建正确地反映到主内存中,这在多线程环境中很重要。

请参考 IBM 的 tutorial 单例+延迟加载+多线程环境案例

===============编辑于 09/09/2018=====================

您还应该查看按需创建对象模式here

【讨论】:

    【解决方案2】:

    对于延迟加载单例实例,我使用如下。

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

    【讨论】:

    • instance 始终为空。您应该在 if 块中对其进行初始化。不过,这不是线程安全的。
    • instance 字段必须是可变的,否则会创建重复的实例。请参阅 Java 中的双重检查锁定
    【解决方案3】:

    首先,单例模式被过度使用了。如果您想要“某物之一”,您真正想做的是在您选择的 DI 框架中将其声明为单例。这实际上是一个配置驱动的渴望单例,并释放了注入模拟以进行适当测试的选项。

    为什么不延迟加载?除非您的类在构造中具有大量的初始化例程(我认为这也是一种反模式),否则延迟加载没有任何好处和很多缺点。你只是增加了复杂性,如果没有正确完成,可能会破坏你的程序。正确的方法(如果必须的话)是使用 Initialization-on-demand 持有者习语。

    【讨论】:

      【解决方案4】:

      您也可以调用任何其他静态方法或静态成员变量来加载单例实例。

      class Logger {     
         private static Logger instance = new Logger(); 
         public static String LOG_LINE_SEPERATOR =  
            System.getProperty("line.separator");
         public static Logger getInstance() {  
                return instance;     
         } 
      
         public static String logPattern() {
             return null;
         }
      } 
      

      ...

      Logger.LOG_LINE_SEPERATOR; // load Logger instance or
      Logger.logPattern(); // load Logger instance
      

      【讨论】:

      • @Prince。我不明白它如何回答给定的问题。我们也可以在惰性初始化中调用其他静态方法,因为我们不需要实例。我们可以做 class.method()。懒惰和急切的初始化在这里有什么帮助?我错过了什么?
      【解决方案5】:

      由于你提到的原因,这只是一种更复杂的方式,与

      enum Singleton {
          INSTANCE;
      }
      

      仅当您担心类可能会被初始化但您不想在此时加载单例时,使用延迟初始化才有用。在大多数情况下,这太过分了。

      注意:仅引用类不会初始化类。

      例如假设您有一个写得不好的类,在设置某些条件之前无法初始化。在这种情况下,n 必须为非零。

      public class Main {
          public static void main(String ... args) {
              Class c= LazyLoaded.class;
              System.out.println(c);
          }
      
          static class LazyLoaded {
              static int n = 0;
              static {
                  System.out.println("Inverse "+1000/n);
              }
          }
      }
      

      打印

      class Main$LazyLoaded
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多