29

我正在做一些关于单例的研究,特别是关于单例的惰性初始化和急切初始化。

急切初始化的示例:

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 时执行,这会处理延迟实例化,并保证是线程安全的。

它是否正确?

4

5 回答 5

25

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

按需初始化持有者成语

我建议你使用它。

于 2013-04-03T16:03:06.857 回答
9

你的第一个设计实际上是懒惰的。想想看,只有类初始化时才创建实例;该类仅在调用该方法时才被初始化getSingleton()[1]。所以实例只在被请求时才被创建,即它是惰性创建的。

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

于 2013-04-03T16:35:23.953 回答
3

第二个可读性很差,第一个比较合适。看看这篇文章。它是关于双重检查锁定的,但也会为您提供有关单例多线程的广泛信息。

于 2013-04-03T16:01:28.730 回答
1

最好的方法实际上是使用Enum Way

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
            //... perform operation here ...
    }
}
于 2013-04-03T16:00:20.300 回答
1

在我看来,这是一个不合适的模式。它对 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 最初所做的那样。
于 2013-04-04T09:27:02.583 回答