Singleton模式结合了延迟加载和线程安全性

我正在做一些关于单身人士的研究,特别是关于单身人士的懒惰与急切初始化。

急切初始化的一个例子:

public class Singleton { //initialzed during class loading private static final Singleton INSTANCE = new Singleton(); //to prevent creating another instance of Singleton private Singleton(){} public static Singleton getSingleton(){ return INSTANCE; } } 

但如上所示,它是急切的初始化和线程安全留给jvm但现在,我希望有相同的模式,但延迟初始化。

所以我想出了这个方法:

 public final class Foo { private static class FooLoader { private static final Foo INSTANCE = new Foo(); } private Foo() { if (FooLoader.INSTANCE != null) { throw new IllegalStateException("Already instantiated"); } } public static Foo getInstance() { return FooLoader.INSTANCE; } } 

如上图所示

 private static final Foo INSTANCE = new Foo(); 

只有在实际使用类FooLoader时才会执行,这会处理惰性实例化,并保证它是线程安全的。

它是否正确?

在我看来,你的第二个代码片段是线程安全的懒惰初始化单例的最佳方式。 它实际上有一个模式名称

初始化按需持有人成语

我建议你使用它。

你第一次设计实际上是懒惰的。 想想看,实例只在初始化类时创建; 只有在getSingleton()方法时才会初始化类[1]。 因此,实例仅在被要求时创建,即它是懒惰创建的。

[1] http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

第二个在可读性方面非常差,第一个是合适的。 看看这篇文章 。 它关于双重检查锁定,但也将为您提供有关单例multithreading的广泛信息。

最好的方法是使用Enum方式 :

 public enum Singleton { INSTANCE; public void execute (String arg) { //... perform operation here ... } } 

在我看来,这是一种不合适的模式。 它对JVM的行为进行了假设,这些假设非常重要且令人困惑。 此外,它有一个虚拟类。 应尽可能避免使用虚拟课程。

我建议采用直截了当的方法:

 public class Foo { private volatile static final Foo instance = null; private Foo() { } public static Foo instance() { if (instance == null) instance = new Foo(); return instance; } } } 

…虽然,这不是原样 – 它不是线程安全的..你真正想要的是Bloch的Effective Java第71项中提出的复核模式; 看到这里 。 根据您案例的链接调整示例,我们得到:

 public class Foo { private volatile static final Foo instance = null; private Foo() { } public static Foo instance() { if (instance != null) return instance; synchronized(instance) { Foo result = instance; if (instance == null) { result = instance = new Foo(); return result; } } } 

笔记:

  • 不要担心这段代码的性能,现代JVM会照顾它,而且它很好。 毕竟, 过早优化是万恶之源 。
  • 正如在其他答案中所建议的那样,上面不是Bloch的首选解决方案,但我认为使用单例的枚举在语义上是不恰当的,就像OP最初所做的那样。