Java Singleton设计模式:问题

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

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

  1. 我们可以用多少种方法在Java中实现Singleton Design Pattern?
  2. Singleton Object的范围是什么?它在JVM中的实际工作方式是什么? 我知道我们总是会有一个Singleton Object的实例,但是该对象的实际范围是什么,是在JVM中还是运行多个应用程序而不是它的范围是在JVM中的每个上下文基础上,我真的很难过无法给出令人满意的解释?
  3. 最后他问是否可以使用带有Clusters的Singleton Object和解释,当我们调用Bean Factory来获取对象时,有没有办法让Spring不实现Singleton Design Pattern?

关于Singleton的任何意见都会受到高度赞赏,在处理Singletons时要记住哪些主要内容?

谢谢。

在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”完成。

我不同意@irreputable。

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

理解范围的这个概念很重要,特别是在具有复杂的类加载器层次结构的应用程序服务器中。

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

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

我发现很难相信这么多答案错过了单身人士的最佳标准练习 – 使用Enums – 这会给你一个单例,其范围是类加载器,对于大多数目的而言足够好。

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

至于更高层次的单身人士 – 也许我很傻 – 但我倾向于分配JVM本身(并限制类加载器)。 然后enum就足够了。

Singleton通常通过具有静态实例对象( private SingletonType SingletonType.instance )来实现,该对象通过静态SingletonType SingletonType.getInstance()方法延迟实例化。 使用单身人士存在许多陷阱,事实上,许多人认为单身人士是设计反模式。 鉴于有关Spring的问题,面试官可能不仅希望了解单身人士,还要了解他们的陷阱以及这些陷阱的解决方法,称为dependency injection。 您可以在Google Guice页面上找到该video,这有助于了解单身人士的陷阱以及DI如何解决这个问题。

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

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

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

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

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

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

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

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

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

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

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

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

以下代码来自此处

关键点是你应该Override clone方法……维基百科的例子也很有帮助。

 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; } 

查询1:

创建Singleton的不同方法

  1. Normal Singleton :静态初始化
  2. ENUM
  3. Lazy Singleton :双重锁定Singleton&:Initialization-on-demand_holder_idiom单例

看看下面的代码:

 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问题:

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

查询2:

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

 private Object readResolve() { return instance; } 

查询3:

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

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. 首次使用时初始化(或Lazy初始化)

     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; } }