Java Singleton设计模式:问题

时间:2010-08-07 00:10:15

标签: java spring design-patterns singleton

我最近接受了采访,他向我询问了Singleton Design Patterns有关它们是如何实现的,我告诉他使用静态变量和静态方法我们可以实现Singleton Design Patterns。

他似乎对答案感到满意,但我想知道

  1. 我们有多少种不同的方式 实现Singleton设计模式 在Java?
  2. Singleton Object的范围是什么?它在JVM中的实际工作方式是什么?我知道我们总是有一个Singleton Object的实例,但是该对象的实际范围是什么,是在JVM中还是运行多个应用程序而不是它的范围是在JVM中的每个上下文基础上,我真的很难过无法给出令人满意的解释?
  3. 最后他问是否可以使用带有Clusters的Singleton Object和解释,当我们调用Bean Factory来获取对象时,有没有办法让Spring不实现Singleton Design Pattern?
  4. 任何输入都会对Singleton表示高度赞赏,在处理Singletons时要记住哪些主要内容?

    感谢。

11 个答案:

答案 0 :(得分:15)

在Java中实现Singleton模式有几种方法:

// private constructor, public static instance
// usage: Blah.INSTANCE.someMethod();
public class Blah {
    public static final Blah INSTANCE = new Blah();
    private Blah() {
    }
    // public methods
}

// private constructor, public instance method
// usage: Woo.getInstance().someMethod();
public class Woo {
    private static final Woo INSTANCE = new Woo();
    private Woo() {
    }
    public static Woo getInstance() {
        return INSTANCE;
    }
    // public methods
}

// Java5+ single element enumeration (preferred approach)
// usage: Zing.INSTANCE.someMethod();
public enum Zing {
    INSTANCE;
    // public methods
}

鉴于上面的示例,每个类加载器将有一个实例。

关于在群集中使用单例...我不确定“使用”的定义是什么...是面试者暗示在整个群集中创建单个实例?我不确定这是否有意义......?

最后,在spring中定义一个非单例对象只需通过属性singleton =“false”来完成。

答案 1 :(得分:2)

我不同意@irreputable。

Singleton的范围是Classloader树中的节点。它包含类加载器,任何子类加载器都可以看到Singleton。

了解范围概念非常重要,尤其是在具有复杂类加载器层次结构的应用程序服务器中。

例如,如果您在应用服务器的系统类路径上的jar文件中有一个库,并且该库使用Singleton,则Singleton将(可能)对于部署到的每个“app”都相同应用服务器。这可能是也可能不是一件好事(取决于图书馆)。

类加载器,恕我直言,是Java和JVM中最重要的概念之一,而Singletons正在发挥作用,所以我认为对Java程序员来说“关心”非常重要。

答案 2 :(得分:2)

我发现很难相信这么多答案错过了单身人士的最佳标准练习 - 使用Enums - 这将为你提供一个单例,其范围是类加载器,对于大多数用途来说足够好。

public enum Singleton { ONE_AND_ONLY_ONE ; ... members and other junk ... }

对于更高级别的单身人士 - 也许我很傻 - 但我的倾向是分发JVM本身(并限制类加载器)。然后enum就足够了。

答案 3 :(得分:1)

Singleton通常通过使用静态实例对象(private SingletonType SingletonType.instance)来实现,该实例对象通过静态SingletonType SingletonType.getInstance()方法进行了懒惰实例化。使用单身人士存在许多陷阱,事实上,许多人认为单身人士是设计反模式。鉴于有关Spring的问题,面试官可能正在寻找对单身人士的理解,以及他们的陷阱以及这些陷阱的解决方案,称为依赖注入。您可以在Google Guice页面上找到视频,这有助于了解单身人士的陷阱以及DI如何解决此问题。

答案 4 :(得分:1)

  

3:最后他问是否可以使用带有Clusters的Singleton Object和解释,当我们调用Bean Factory来获取对象时,有没有办法让Spring不实现Singleton Design Pattern?

如果没有技术背景,这个问题的第一部分很难回答。如果集群平台包括对远程对象进行调用的能力,就好像它们是本地对象一样(例如,使用引擎盖下的RMI或IIOP的EJB可能),那么可以这样做。例如,JVM常驻单例对象可以是群集范围的单例对象的代理,最初通过JNDI或其他东西定位/连接。但群集范围内的单例是一个潜在的瓶颈,因为对单个代理之一的每次调用都会导致(昂贵的)RPC到单个远程对象。

问题的第二部分是Spring Bean Factories可以配置不同的范围。默认值是单例(在webapp级别作用域),但它们也可以是会话或请求作用域,或者应用程序可以定义自己的作用域机制。

答案 5 :(得分:0)

静态字段在一个JVM中可以多次出现 - 通过使用差异类加载器,可以多次加载和初始化相同的类,但每个都处于隔离状态,JVM将结果加载的类视为完全不同的类。

我不认为Java程序员应该关心,除非他正在编写一些框架。 “每个虚拟机一个”是一个很好的答案。人们经常这样说,而严格来说他们说的是“每个班级一个人”。

每个群集可以有一个单身人士吗?那是一个概念游戏。我不会欣赏采访者这样说的话。

答案 6 :(得分:0)

  1. 您已经涵盖了标准方式。此外,大多数依赖注入方案都有一些方法将类标记为单例;这样,该类看起来就像任何其他类一样,但是框架确保当您注入该类的实例时,它始终是相同的实例。

  2. 这就是毛茸茸的地方。例如,如果在Tomcat应用程序上下文中初始化类,则单例实例的生命周期将绑定到该上下文。但是很难预测你的类将被初始化的位置;所以最好不做任何假设。如果你想绝对确保每个上下文只有一个实例,你应该将它绑定为ServletContext的一个属性。 (或者让依赖注入框架来处理它。)

  3. -

  4. 我不确定我是否理解这个问题 - 但如果您正在谈论在多个集群节点之间共享一个单例实例,那么我认为EJB使这成为可能(通过远程bean),尽管我已经从未尝试过。不知道Spring是如何做到的。

答案 7 :(得分:0)

Singleton是一种创造模式,因此可以控制对象的实例化。创建单例会强制您自愿或非自愿地放弃对创建对象的控制,而是依靠某种方式来获取对象。

这可以使用静态方法或依赖注入或使用工厂模式来实现。手段无关紧要。在普通受保护构造函数()方法的情况下,使用者perforce需要使用静态方法来访问单例。在DI的情况下,消费者自愿放弃对类的实例化的控制,而是依赖于DI框架将实例注入其自身。

正如其他海报所指出的,java中的类加载器将定义单例的范围。群集中的单身人士通常是“非单一实例”,而是表现出类似行为的实例集合。这些可以是SOA中的组件。

答案 8 :(得分:0)

以下代码来自here

关键点是你应该Override clone方法......维基百科example也很有帮助。

public class SingletonObject
{
  private SingletonObject()
  {
    // no code req'd
  }

  public static SingletonObject getSingletonObject()
  {
    if (ref == null)
        // it's ok, we can call this constructor
        ref = new SingletonObject();        
    return ref;
  }

  public Object clone()
    throws CloneNotSupportedException
  {
    throw new CloneNotSupportedException(); 
    // that'll teach 'em
  }

  private static SingletonObject ref;
}

答案 9 :(得分:0)

查询1:

创建Singleton的不同方式

  1. Normal Singleton:静态初始化
  2. ENUM
  3. Lazy Singleton:双锁单身& :Initialization-on-demand_holder_idiom singleton
  4. 看看下面的代码:

    public final class Singleton{
        private static final Singleton instance = new Singleton();
    
        public static Singleton getInstance(){
            return instance; 
        }
        public enum EnumSingleton {
            INSTANCE;   
        }   
        public static void main(String args[]){
            System.out.println("Singleton:"+Singleton.getInstance());
            System.out.println("Enum.."+EnumSingleton.INSTANCE);
            System.out.println("Lazy.."+LazySingleton.getInstance());
        }
    }
    final class LazySingleton {
        private LazySingleton() {}
        public static LazySingleton getInstance() {
            return LazyHolder.INSTANCE;
        }
        private static class LazyHolder {
            private static final LazySingleton INSTANCE = new LazySingleton();
        }
    }
    

    相关的SE问题:

    What is an efficient way to implement a singleton pattern in Java?

    查询2:

    每个ClassLoader创建一个Singleton实例。如果您想避免在Singleton期间创建Serializaiton对象,请覆盖以下方法并返回相同的实例。

    private Object readResolve()  { 
        return instance; 
    }
    

    查询3:

    要在多个服务器之间实现群集级别Singleton,请将此Singleton对象存储在TerracottaCoherence等分布式缓存中。

答案 10 :(得分:0)

Singleton是一种创造性的设计模式。

Singleton设计模式的意图:

  • 确保一个类只有一个实例,并提供全局点 访问它。
  • 封装"即时初始化"或者"初始化 首先使用"。

我在这里展示了三种类型的实现。

  1. 及时初始化(在第一次运行期间分配内存,即使您不使用它)

    class Foo{
    
        // Initialized in first run
        private static Foo INSTANCE = new Foo();
    
        /**
        * Private constructor prevents instantiation from outside
        */
        private Foo() {}
    
        public static Foo getInstance(){
            return INSTANCE;
        }
    
    }
    
  2. 首次使用时初始化(或延迟初始化)

    class Bar{
    
        private static Bar instance;
    
        /**
        * Private constructor prevents instantiation from outside
        */
        private Bar() {}
    
        public static Bar getInstance(){
    
            if (instance == null){
                // initialized in first call of getInstance()
                instance = new Bar();
            }
    
            return instance;
        }
    }
    
  3. 这是另一种Lazy初始化方式,但优点是,此解决方案是线程安全的,无需特殊的语言结构(即volatile或synchronized)。阅读更多SourceMaking.com

    class Blaa{
    
        /**
         * Private constructor prevents instantiation from outside
         */
        private Blaa() {}
    
        /**
         * BlaaHolder is loaded on the first execution of Blaa.getInstance()
         * or the first access to SingletonHolder.INSTANCE, not before.
         */
        private static class BlaaHolder{
            public static Blaa INSTANCE = new Blaa();
        }
    
        public static Blaa getInstance(){
            return BlaaHolder.INSTANCE;
        }
    
    }