在Java中实现单例设计模式的有效方法是什么?


当前回答

维基百科有一些单态的例子,也在Java中。Java5实现看起来非常完整,并且是线程安全的(应用了双重检查锁定)。

其他回答

我对一些答案感到困惑,这些答案建议依赖注入(DI)作为使用单态的替代方案;这些都是不相关的概念。您可以使用DI注入单例或非单例(例如,每个线程)实例。至少如果您使用Spring2.x,这是正确的,我不能为其他DI框架说话。

所以我对OP的回答是(除了最简单的示例代码之外):

使用类似Spring framework的DI框架,然后将其作为DI配置的一部分,无论依赖项是单体的、请求范围的、会话范围的还是其他。

这种方法为您提供了一个很好的解耦(因此是灵活和可测试的)架构,其中是否使用单例是一个容易可逆的实现细节(当然,前提是您使用的任何单例都是线程安全的)。

对于JSE 5.0及以上版本,采用Enum方法。否则,使用静态单例持有者方法(Bill Pugh描述的一种延迟加载方法)。后一种解决方案也是线程安全的,不需要特殊的语言构造(即易失性或同步)。

确保你真的需要它。在谷歌上搜索“singleton反模式”,看看反对它的一些论点。

我想这并没有什么本质上的问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是,我发现依赖注入(DI)更有用,特别是如果您也在使用单元测试,因为DI允许您将模拟资源用于测试目的。

public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        if (INSTANCE != null)
            throw new IllegalStateException(“Already instantiated...”);
        }


    public synchronized static Singleton getInstance() {
        return INSTANCE;
    }

}

由于我们在getInstance之前添加了Synchronized关键字,因此在两个线程同时调用getInstance的情况下,我们避免了竞争条件。

如果需要惰性地加载类的实例变量,则需要双重检查习惯用法。如果需要惰性地加载静态变量或单例,则需要按需初始化持有者习惯用法。

此外,如果单例需要可序列化,则所有其他字段都需要是暂时的,并且需要实现readResolve()方法以保持单例对象不变。否则,每次反序列化对象时,都会创建对象的新实例。readResolve()所做的是用readObject()替换读取的新对象,因为没有变量引用该对象,所以强制对该新对象进行垃圾回收。

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // Original singleton instance.
}