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最初所做的那样。