WHAT

定义

Ensure a class has only one instance, and provice a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)

属性

创建型模式的一种

Singleton类 - 单例类

通过使用private的构造函数确保在一个应用中只产生一个实例,并且是自行实例化的

实现思路

一个类能返回对象一个应用(永远是同一个)和一个活的该实例的方法(必须是静态方法,通过使用getInstance这个名称)。

调用getInstance方法时,如果类持有的引用不为空,就返回这个引用;如果类持有的引用为空就创建该类的实例并将实例的引用赋予该类持有的引用。

将该类的构造函数定义为私有方法。只有通过该类提供的静态方法才能获得该类的唯一实例

构造方式

  • 懒汉方式。全局的单例实例在第一次被使用时构建

    public class Singleton {
    	private static final Singleton INSTANCE = new Singleton();
    
    	// Private constructor suppresses
    	// default public constructor
    	private Single(){};
    
    	public static Singleton getInstance(){
    		return INSTANCE;
    	}
    }
    
  • 饿汉方式。全局的单例实例在类装载时构建

    public class Singleton {
      private static volative Singleton INSTANCE = null;
    
      // private constructor suppresses;
      // default public constructor
      private Singleton() {};
    
      //Thread safe and performance promote
      public static Singleton getInstance(){
        if(INSTANCE == null){
          synchronized(Singleton.class){
            // when more than two threads run into the first null check same time,
            // to avoid instanced more than one time, it needs to be checked again.
            if(INSTANCE == null){
              INSTANCE == new Singleton();
            }
          }
        }
        return INSTANCE;
      }
    }
    

WHY

优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势
  • 只生成一个实例,所以减少了系统的性能开销。当一个对象的产生需要比较的资源时,如读取配置、产生其他依赖对象时,可以通过在应用启动时直接产生一个单例对昂,永久驻留内存的方式来解决
  • 可以避免对资源的多重占用。一个写文件的动作,只有一个实例,可以避免对同一个资源文件同时执行写操作
  • 可以在系统设置全局的访问点,优化和共享资源访问。
  • 简单使用可以确保线程安全

缺点

  • 一般没有接口,扩展困难,基本上只有通过修改代码实现扩展
    • 接口对单例模式没有意义,单例模式要求“自行实例化”
  • 对测试是不利的
  • 与单一职责原则有冲突

HOW

使用场景

在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象会有不好反应时,可以采用单例模式。

  • 要求生成唯一序列号的环境
  • 需要一个共享访问点或共享数据
  • 创建一个对象需要消耗的资源过多,如访问IO和数据库等资源
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式

注意事项

高并发情况

需要注意单例模式的线程同步问题。

解决方案

在使用饿汉方式使用单例模式时,需要注意使用synchronized关键字来保证不会重复创建多个实例。

对象的复制情况

对象默认是不可以被复制的。如果实现了Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象。

对象复制是不用调用类的构造函数,因此即使是私有的构造函数,对象仍然可以被复制。

解决方案

单例类不要实现Cloneable接口。

扩展

如果要求一个类只能产生两三个对象呢?

这种需要产生固定数量对象的模式叫有上限的多例模式,是单例模式的一种扩展,采用有上限的多例模式,可以在设计时决定内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提升系统的响应速度。

最佳实践

  • 在Spring中,每个Bean默认是单例的
    • Spring容器可以管理这些Bean的生命周期

《设计模式之禅》第七章 单例模式

wikipedia - 单例模式