Singleton lazy vs eager instantiation

时间:2011-10-17 06:39:25

标签: java singleton lazy-initialization

如果单个实现如下,

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

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

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

即使使用延迟初始化方法,也会在调用getInstance()

时创建实例

我在这里错过了什么吗?

6 个答案:

答案 0 :(得分:18)

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

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

请参阅IBM的tutorial关于Singleton + Lazy Loading +多线程环境案例

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

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

答案 1 :(得分:13)

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

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

答案 2 :(得分:7)

基于你提到的原因,这只是一种比

更复杂的做法
enum Singleton {
    INSTANCE;
}

使用延迟初始化仅在您担心类可能已初始化但您不想在此时加载单例时才有用。在大多数情况下,这都是过度杀戮。

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

e.g。假设您有一个编写得很糟糕的类,在设置某些条件之前无法初始化。在这种情况下,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

答案 3 :(得分:1)

首先,单例模式被过度使用。如果你想要“一件事”,你真正想要做的就是在你选择的DI框架中宣布它是一个单身人士。这实际上是一个配置驱动的急切单例,并释放注入模拟的选项以进行适当的测试。

为什么不加载延迟?除非你的类在构造中有一个大规模的初始化例程(我认为这也是一个反模式),所以延迟加载没有任何好处和许多缺点。如果没有正确完成,你只是增加了复杂性并可能破坏你的程序。正确的方法(如果必须)是使用按需初始化持有者惯用法。

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

急于加载单例设计模式并不是一个过程,在该过程中,我们需要在应用程序启动时而不是按需初始化单例对象,并将其保留在内存中以备将来使用。在Singleton设计模式中使用“急切加载”的优点是CLR(公共语言运行时)将负责对象初始化和线程安全性。这意味着我们将不需要显式编写任何代码来处理多线程环境的线程安全。

“延迟加载”或“延迟加载”是一种设计模式,或者您可以说它是一个概念,通常用于将对象的初始化延迟到需要的时间点。因此,延迟加载的主要目标是按需加载对象,或者可以在需要时说对象。您需要牢记的最重要的一点是,当对象创建的成本很高且该对象的使用非常少时,您需要使用延迟加载。如果正确使用延迟加载,则可以提高应用程序的性能。

请参考以下点网教程,在其中可以找到两者的清晰示例。

https://dotnettutorials.net/lesson/lazy-vs-eager-loading-chsrap/